001: /*
002: * Copyright 2006-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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: package org.kuali.module.gl.util;
017:
018: import java.io.FileOutputStream;
019: import java.text.DecimalFormat;
020: import java.text.SimpleDateFormat;
021: import java.util.Collection;
022: import java.util.Date;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.SortedMap;
026: import java.util.TreeMap;
027:
028: import org.kuali.core.util.KualiDecimal;
029:
030: import com.lowagie.text.Document;
031: import com.lowagie.text.Element;
032: import com.lowagie.text.Font;
033: import com.lowagie.text.FontFactory;
034: import com.lowagie.text.PageSize;
035: import com.lowagie.text.Phrase;
036: import com.lowagie.text.pdf.PdfPCell;
037: import com.lowagie.text.pdf.PdfPTable;
038: import com.lowagie.text.pdf.PdfWriter;
039:
040: /**
041: * Represents a ledger report
042: *
043: */
044: public class LedgerReport {
045: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
046: .getLogger(LedgerReport.class);
047: public static final String PDF_FILE_EXTENSION = ".pdf";
048:
049: private Font headerFont = FontFactory.getFont(FontFactory.COURIER,
050: 8, Font.BOLD);
051: private Font textFont = FontFactory.getFont(FontFactory.COURIER, 8,
052: Font.NORMAL);
053: private Font totalFieldFont = FontFactory.getFont(
054: FontFactory.COURIER, 8, Font.BOLD);
055:
056: /**
057: * This method generates report based on the given map of ledger entries
058: *
059: * @param ledgerEntries the given ledger entry map
060: * @param reportingDate the reporting date
061: * @param title the report title
062: * @param fileprefix the prefix of the generated report file
063: * @param destinationDirectory the directory where the report is located
064: */
065: public void generateReport(Map ledgerEntries, Date runDate,
066: String title, String fileprefix, String destinationDirectory) {
067: LOG.debug("generateReport() started");
068: this .generateReport(ledgerEntries.values(), runDate, title,
069: fileprefix, destinationDirectory);
070: }
071:
072: /**
073: * This method generates report based on the given collection of ledger entries
074: *
075: * @param entryCollection the given collection of ledger entries
076: * @param reportingDate the reporting date
077: * @param title the report title
078: * @param fileprefix the prefix of the generated report file
079: * @param destinationDirectory the directory where the report is located
080: */
081: public void generateReport(Collection entryCollection,
082: Date reportingDate, String title, String fileprefix,
083: String destinationDirectory) {
084: LOG.debug("generateReport() started");
085: this .generatePDFReport(entryCollection, reportingDate, title,
086: fileprefix, destinationDirectory);
087: }
088:
089: /**
090: * This method generates report based on the given holder of ledger entries
091: *
092: * @param ledgerEntryHolder the given holder of ledger entries
093: * @param reportingDate the reporting date
094: * @param title the report title
095: * @param fileprefix the prefix of the generated report file
096: * @param destinationDirectory the directory where the report is located
097: */
098: public void generateReport(LedgerEntryHolder ledgerEntryHolder,
099: Date reportingDate, String title, String fileprefix,
100: String destinationDirectory) {
101: LOG.debug("generateReport() started");
102: this .generatePDFReport(ledgerEntryHolder, reportingDate, title,
103: fileprefix, destinationDirectory);
104: }
105:
106: /**
107: * Generate the PDF report with the given information
108: *
109: * @param ledgerEntryHolder the given holder of ledger entries
110: * @param reportingDate the reporting date
111: * @param title the report title
112: * @param fileprefix the prefix of the generated report file
113: * @param destinationDirectory the directory where the report is located
114: */
115: private void generatePDFReport(Object ledgerEntryHolder,
116: Date reportingDate, String title, String fileprefix,
117: String destinationDirectory) {
118: Document document = new Document(PageSize.A4.rotate());
119:
120: PDFPageHelper pageHelper = new PDFPageHelper();
121: pageHelper.setRunDate(reportingDate);
122: pageHelper.setHeaderFont(headerFont);
123: pageHelper.setTitle(title);
124:
125: try {
126: String filename = destinationDirectory + "/" + fileprefix
127: + "_";
128: SimpleDateFormat sdf = new SimpleDateFormat(
129: "yyyyMMdd_HHmmss");
130: filename = filename + sdf.format(reportingDate);
131: filename = filename + PDF_FILE_EXTENSION;
132:
133: PdfWriter writer = PdfWriter.getInstance(document,
134: new FileOutputStream(filename));
135: writer.setPageEvent(pageHelper);
136:
137: document.open();
138: PdfPTable ledgerEntryTable = this
139: .drawPdfTable(ledgerEntryHolder);
140: document.add(ledgerEntryTable);
141: } catch (Exception de) {
142: LOG.error("generateReport() Error creating PDF report", de);
143: throw new RuntimeException("Report Generation Failed");
144: } finally {
145: this .closeDocument(document);
146: }
147: }
148:
149: /**
150: * draw a PDF table populated with the data held by ledger entry holder
151: *
152: * @param ledgerEntryHolder the given holder of ledger entries
153: * @return PdfTable PDF table containing ledger entry information
154: */
155: public PdfPTable drawPdfTable(Object ledgerEntryHolder) {
156: PdfPTable ledgerEntryTable = null;
157: if (ledgerEntryHolder instanceof Collection) {
158: ledgerEntryTable = this
159: .buildPdfTable((Collection) ledgerEntryHolder);
160: } else if (ledgerEntryHolder instanceof LedgerEntryHolder) {
161: ledgerEntryTable = this
162: .buildPdfTable((LedgerEntryHolder) ledgerEntryHolder);
163: }
164: return ledgerEntryTable;
165: }
166:
167: /**
168: * Draw a PDF table from a collection
169: * @param entryCollection collection of ledger entries
170: * @return PdfTable PDF table containing information about the collection of entries
171: */
172: private PdfPTable buildPdfTable(Collection entryCollection) {
173:
174: if (entryCollection == null || entryCollection.size() <= 0) {
175: return this .buildEmptyTable();
176: }
177:
178: float[] warningWidths = { 3, 3, 6, 3, 8, 10, 8, 10, 8, 10, 8 };
179: PdfPTable ledgerEntryTable = new PdfPTable(warningWidths);
180: ledgerEntryTable.setHeaderRows(1);
181: ledgerEntryTable.setWidthPercentage(100);
182:
183: this .addHeader(ledgerEntryTable, headerFont);
184:
185: for (Iterator reportIter = entryCollection.iterator(); reportIter
186: .hasNext();) {
187: LedgerEntry ledgerEntry = (LedgerEntry) reportIter.next();
188: this .addRow(ledgerEntryTable, ledgerEntry, textFont, false);
189: }
190:
191: return ledgerEntryTable;
192: }
193:
194: /**
195: * draw a PDF table from ledger entry holder
196: * @param ledgerEntryHolder the given holder of ledger entries
197: * @return PdfTable PDF table containing information about the ledget entry holder
198: */
199: private PdfPTable buildPdfTable(LedgerEntryHolder ledgerEntryHolder) {
200: SortedMap ledgerEntries = new TreeMap(ledgerEntryHolder
201: .getLedgerEntries());
202: Collection entryCollection = ledgerEntries.values();
203: Map subtotalMap = ledgerEntryHolder.getSubtotals();
204:
205: if (entryCollection == null || entryCollection.size() <= 0) {
206: return this .buildEmptyTable();
207: }
208:
209: float[] warningWidths = { 3, 3, 6, 3, 8, 10, 8, 10, 8, 10, 8 };
210: PdfPTable ledgerEntryTable = new PdfPTable(warningWidths);
211: ledgerEntryTable.setHeaderRows(1);
212: ledgerEntryTable.setWidthPercentage(100);
213:
214: this .addHeader(ledgerEntryTable, headerFont);
215:
216: String tempBalanceType = "--";
217: for (Iterator reportIter = entryCollection.iterator(); reportIter
218: .hasNext();) {
219: LedgerEntry ledgerEntry = (LedgerEntry) reportIter.next();
220:
221: // add the subtotal rows
222: if (!ledgerEntry.getBalanceType().equals(tempBalanceType)) {
223: if (subtotalMap.containsKey(tempBalanceType)) {
224: LedgerEntry subtotal = (LedgerEntry) subtotalMap
225: .get(tempBalanceType);
226: this .addRow(ledgerEntryTable, subtotal,
227: totalFieldFont, true);
228: }
229: tempBalanceType = ledgerEntry.getBalanceType();
230: }
231: this .addRow(ledgerEntryTable, ledgerEntry, textFont, false);
232:
233: // deal with the subtotal after adding the last row
234: if (!reportIter.hasNext()
235: && subtotalMap.containsKey(tempBalanceType)) {
236: LedgerEntry subtotal = (LedgerEntry) subtotalMap
237: .get(tempBalanceType);
238: this .addRow(ledgerEntryTable, subtotal, totalFieldFont,
239: true);
240: }
241: }
242: this
243: .addRow(ledgerEntryTable, ledgerEntryHolder
244: .getGrandTotal(), totalFieldFont, true);
245:
246: return ledgerEntryTable;
247: }
248:
249: /**
250: * Draw a table with an informative messge, instead of data
251: * @return PdfTable empty PDF table
252: */
253: private PdfPTable buildEmptyTable() {
254: float[] tableWidths = { 100 };
255:
256: PdfPTable ledgerEntryTable = new PdfPTable(tableWidths);
257: ledgerEntryTable.setWidthPercentage(100);
258: PdfPCell cell = new PdfPCell(new Phrase("No entries found!",
259: headerFont));
260: ledgerEntryTable.addCell(cell);
261:
262: return ledgerEntryTable;
263: }
264:
265: /**
266: * Add a table header
267: *
268: * @param ledgerEntryTable PDF table containing ledger entry information
269: * @param headerFont font for header
270: */
271: private void addHeader(PdfPTable ledgerEntryTable, Font headerFont) {
272:
273: PdfPCell cell = new PdfPCell(new Phrase("BAL TYP", headerFont));
274: ledgerEntryTable.addCell(cell);
275:
276: cell = new PdfPCell(new Phrase("ORIG", headerFont));
277: ledgerEntryTable.addCell(cell);
278:
279: cell = new PdfPCell(new Phrase("YEAR", headerFont));
280: ledgerEntryTable.addCell(cell);
281:
282: cell = new PdfPCell(new Phrase("PRD", headerFont));
283: ledgerEntryTable.addCell(cell);
284:
285: cell = new PdfPCell(new Phrase("Record Count", headerFont));
286: ledgerEntryTable.addCell(cell);
287:
288: cell = new PdfPCell(new Phrase("Debit Amount", headerFont));
289: ledgerEntryTable.addCell(cell);
290:
291: cell = new PdfPCell(new Phrase("Debit Count", headerFont));
292: ledgerEntryTable.addCell(cell);
293:
294: cell = new PdfPCell(new Phrase("Credit Amount", headerFont));
295: ledgerEntryTable.addCell(cell);
296:
297: cell = new PdfPCell(new Phrase("Credit Count", headerFont));
298: ledgerEntryTable.addCell(cell);
299:
300: cell = new PdfPCell(
301: new Phrase("No D/C Code Amount", headerFont));
302: ledgerEntryTable.addCell(cell);
303:
304: cell = new PdfPCell(new Phrase("No D/C Code Count", headerFont));
305: ledgerEntryTable.addCell(cell);
306: }
307:
308: /**
309: * Add a row with the given ledger entry into PDF table
310: *
311: * @param ledgerEntryTable PDF table containing ledger entry information
312: * @param ledgerEntry ledger entry
313: * @param textFont font for text
314: * @param isTotal if added row is total row or not
315: */
316: private void addRow(PdfPTable ledgerEntryTable,
317: LedgerEntry ledgerEntry, Font textFont, boolean isTotal) {
318: PdfPCell cell = null;
319: if (isTotal) {
320: String balanceType = ledgerEntry.getBalanceType() != null ? "("
321: + ledgerEntry.getBalanceType() + ")"
322: : "";
323: String totalDescription = ledgerEntry.getOriginCode()
324: + balanceType + ":";
325:
326: cell = new PdfPCell(new Phrase(totalDescription, textFont));
327: cell.setColspan(4);
328: ledgerEntryTable.addCell(cell);
329: } else {
330: cell = new PdfPCell(new Phrase(
331: ledgerEntry.getBalanceType(), textFont));
332: ledgerEntryTable.addCell(cell);
333:
334: cell = new PdfPCell(new Phrase(ledgerEntry.getOriginCode(),
335: textFont));
336: ledgerEntryTable.addCell(cell);
337:
338: String fiscalYear = (ledgerEntry.getFiscalYear() != null) ? ledgerEntry
339: .getFiscalYear().toString()
340: : "";
341: cell = new PdfPCell(new Phrase(fiscalYear, textFont));
342: ledgerEntryTable.addCell(cell);
343:
344: cell = new PdfPCell(new Phrase(ledgerEntry.getPeriod(),
345: textFont));
346: ledgerEntryTable.addCell(cell);
347: }
348:
349: cell = new PdfPCell(new Phrase(this .formatNumber(new Integer(
350: ledgerEntry.getRecordCount())), textFont));
351: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
352: ledgerEntryTable.addCell(cell);
353:
354: cell = new PdfPCell(new Phrase(this .formatNumber(ledgerEntry
355: .getDebitAmount()), textFont));
356: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
357: ledgerEntryTable.addCell(cell);
358:
359: cell = new PdfPCell(new Phrase(this .formatNumber(new Integer(
360: ledgerEntry.getDebitCount())), textFont));
361: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
362: ledgerEntryTable.addCell(cell);
363:
364: cell = new PdfPCell(new Phrase(this .formatNumber(ledgerEntry
365: .getCreditAmount()), textFont));
366: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
367: ledgerEntryTable.addCell(cell);
368:
369: cell = new PdfPCell(new Phrase(this .formatNumber(new Integer(
370: ledgerEntry.getCreditCount())), textFont));
371: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
372: ledgerEntryTable.addCell(cell);
373:
374: cell = new PdfPCell(new Phrase(this .formatNumber(ledgerEntry
375: .getNoDCAmount()), textFont));
376: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
377: ledgerEntryTable.addCell(cell);
378:
379: cell = new PdfPCell(new Phrase(this .formatNumber(new Integer(
380: ledgerEntry.getNoDCCount())), textFont));
381: cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
382: ledgerEntryTable.addCell(cell);
383: }
384:
385: /**
386: * Format the given number based on its type: Integer or BigDecimal
387: *
388: * @param number Integer or BigDecimal to format
389: * @return String number in string format
390: */
391: private String formatNumber(Number number) {
392: DecimalFormat decimalFormat = new DecimalFormat();
393:
394: if (number instanceof Integer) {
395: decimalFormat.applyPattern("###,###");
396: } else if (number instanceof KualiDecimal) {
397: decimalFormat.applyPattern("###,###,###,##0.00");
398: }
399: return decimalFormat.format(number);
400: }
401:
402: /**
403: * Close the document and release the resource
404: *
405: * @param document
406: */
407: private void closeDocument(Document document) {
408: try {
409: if ((document != null) && document.isOpen()) {
410: document.close();
411: }
412: } catch (Throwable t) {
413: LOG.error("generateReport() Exception closing report", t);
414: }
415: }
416: }
|