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 package org.kuali.kfs.module.ar.batch.service.impl;
017
018 import java.awt.Color;
019 import java.io.BufferedOutputStream;
020 import java.io.ByteArrayOutputStream;
021 import java.io.File;
022 import java.io.FileOutputStream;
023 import java.io.IOException;
024 import java.io.PrintWriter;
025 import java.text.SimpleDateFormat;
026 import java.util.HashMap;
027 import java.util.Iterator;
028 import java.util.Map;
029
030 import org.apache.commons.lang.StringUtils;
031 import org.apache.log4j.Logger;
032 import org.kuali.kfs.module.ar.ArConstants;
033 import org.kuali.kfs.module.ar.batch.service.LockboxService;
034 import org.kuali.kfs.module.ar.businessobject.AccountsReceivableDocumentHeader;
035 import org.kuali.kfs.module.ar.businessobject.CashControlDetail;
036 import org.kuali.kfs.module.ar.businessobject.Lockbox;
037 import org.kuali.kfs.module.ar.businessobject.SystemInformation;
038 import org.kuali.kfs.module.ar.dataaccess.LockboxDao;
039 import org.kuali.kfs.module.ar.document.CashControlDocument;
040 import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument;
041 import org.kuali.kfs.module.ar.document.PaymentApplicationDocument;
042 import org.kuali.kfs.module.ar.document.service.AccountsReceivableDocumentHeaderService;
043 import org.kuali.kfs.module.ar.document.service.CashControlDocumentService;
044 import org.kuali.kfs.module.ar.document.service.PaymentApplicationDocumentService;
045 import org.kuali.kfs.module.ar.document.service.SystemInformationService;
046 import org.kuali.kfs.sys.KFSConstants;
047 import org.kuali.kfs.sys.context.SpringContext;
048 import org.kuali.rice.kew.docsearch.service.SearchableAttributeProcessingService;
049 import org.kuali.rice.kew.exception.WorkflowException;
050 import org.kuali.rice.kim.bo.Person;
051 import org.kuali.rice.kim.service.PersonService;
052 import org.kuali.rice.kns.UserSession;
053 import org.kuali.rice.kns.service.BusinessObjectService;
054 import org.kuali.rice.kns.service.DataDictionaryService;
055 import org.kuali.rice.kns.service.DateTimeService;
056 import org.kuali.rice.kns.service.DocumentService;
057 import org.kuali.rice.kns.util.GlobalVariables;
058 import org.kuali.rice.kns.util.KualiDecimal;
059 import org.kuali.rice.kns.util.ObjectUtils;
060 import org.springframework.transaction.annotation.Transactional;
061
062 import com.lowagie.text.Chunk;
063 import com.lowagie.text.DocumentException;
064 import com.lowagie.text.Font;
065 import com.lowagie.text.FontFactory;
066 import com.lowagie.text.PageSize;
067 import com.lowagie.text.Paragraph;
068 import com.lowagie.text.pdf.PdfWriter;
069
070 /**
071 *
072 * Lockbox Iterators are sorted by processedInvoiceDate and batchSequenceNumber.
073 * Potentially there could be many batches on the same date.
074 * For each set of records with the same processedInvoiceDate and batchSequenceNumber,
075 * there will be one Cash-Control document. Each record within this set will create one Application document.
076 *
077 */
078
079 @Transactional
080 public class LockboxServiceImpl implements LockboxService {
081 private static Logger LOG = org.apache.log4j.Logger.getLogger(LockboxServiceImpl.class);;
082
083 private PersonService<Person> personService;
084 private DocumentService documentService;
085 private SystemInformationService systemInformationService;
086 private AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService;
087 private CashControlDocumentService cashControlDocumentService;
088 private PaymentApplicationDocumentService payAppDocService;
089 private DataDictionaryService dataDictionaryService;
090 private DateTimeService dateTimeService;
091 private BusinessObjectService boService;
092
093 private LockboxDao lockboxDao;
094 private String reportsDirectory;
095
096 public boolean processLockbox() throws WorkflowException {
097
098 // create the pdf doc
099 com.lowagie.text.Document pdfdoc = getPdfDoc();
100
101 // this giant try/catch is to make sure that something gets written to the
102 // report. please dont use it for specific exception handling, rather nest
103 // new try/catch handlers inside this.
104 try {
105
106 Iterator<Lockbox> itr = lockboxDao.getAllLockboxes();
107 Lockbox ctrlLockbox = new Lockbox();
108 CashControlDocument cashControlDocument = null;
109 boolean anyRecordsFound = false;
110 while (itr.hasNext()) {
111 anyRecordsFound = true;
112 Lockbox lockbox = (Lockbox)itr.next();
113 LOG.info("LOCKBOX: '" + lockbox.getLockboxNumber() + "'");
114
115 // retrieve the processingOrg (system information) for this lockbox number
116 SystemInformation sysInfo = systemInformationService.getByLockboxNumberForCurrentFiscalYear(lockbox.getLockboxNumber());
117 String initiator = sysInfo.getFinancialDocumentInitiatorIdentifier();
118 LOG.info(" using SystemInformation: '" + sysInfo.toString() + "'");
119 LOG.info(" using Financial Document Initiator ID: '" + initiator + "'");
120
121 // puke if the initiator stored in the systemInformation table is no good
122 Person person = getPersonService().getPerson(initiator);
123 if (person == null) {
124 LOG.warn(" could not find [" + initiator + "] when searching by PrincipalID, so trying to find as a PrincipalName.");
125 person = getPersonService().getPersonByPrincipalName(initiator);
126 if (person == null) {
127 LOG.error("Financial Document Initiator ID [" + initiator + "] specified in SystemInformation [" + sysInfo.toString() + "] for Lockbox Number " + lockbox.getLockboxNumber() + " is not present in the system as either a PrincipalID or a PrincipalName.");
128 throw new RuntimeException("Financial Document Initiator ID [" + initiator + "] specified in SystemInformation [" + sysInfo.toString() + "] for Lockbox Number " + lockbox.getLockboxNumber() + " is not present in the system as either a PrincipalID or a PrincipalName.");
129 }
130 else {
131 LOG.info(" found [" + initiator + "] in the system as a PrincipalName.");
132 }
133 }
134 else {
135 LOG.info(" found [" + initiator + "] in the system as a PrincipalID.");
136 }
137
138 // masquerade as the person indicated in the systemInformation
139 GlobalVariables.clear();
140 GlobalVariables.setUserSession(new UserSession(person.getPrincipalName()));
141
142 if (lockbox.compareTo(ctrlLockbox) != 0) {
143 // If we made it in here, then we have hit a different batchSequenceNumber and processedInvoiceDate.
144 // When this is the case, we create a new cashcontroldocument and start tacking subsequent lockboxes on
145 // to the current cashcontroldocument as cashcontroldetails.
146 LOG.info("New Lockbox batch");
147
148 // we're creating a new cashcontrol, so if we have an old one, we need to route it
149 if (cashControlDocument != null) {
150 LOG.info(" routing cash control document.");
151 try {
152 documentService.routeDocument(cashControlDocument, "Routed by Lockbox Batch process.", null);
153 }
154 catch (Exception e) {
155 LOG.error("A Exception was thrown while trying to route the CashControl document.", e);
156 throw new RuntimeException("A Exception was thrown while trying to route the CashControl document.", e);
157 }
158 }
159
160 // create a new CashControl document
161 LOG.info("Creating new CashControl document for invoice: " + lockbox.getFinancialDocumentReferenceInvoiceNumber() + ".");
162 try {
163 cashControlDocument = (CashControlDocument)documentService.getNewDocument(KFSConstants.FinancialDocumentTypeCodes.CASH_CONTROL);
164 }
165 catch (Exception e) {
166 LOG.error("A Exception was thrown while trying to initiate a new CashControl document.", e);
167 throw new RuntimeException("A Exception was thrown while trying to initiate a new CashControl document.", e);
168 }
169 LOG.info(" CashControl documentNumber == '" + cashControlDocument.getDocumentNumber() + "'");
170
171 // write the batch group header to the report
172 writeBatchGroupSectionTitle(pdfdoc, lockbox.getBatchSequenceNumber().toString(), lockbox.getProcessedInvoiceDate(),
173 cashControlDocument.getDocumentNumber());
174
175 cashControlDocument.setCustomerPaymentMediumCode(lockbox.getCustomerPaymentMediumCode());
176 if(ObjectUtils.isNotNull(lockbox.getBankCode())) {
177 String bankCode = lockbox.getBankCode();
178 cashControlDocument.setBankCode(bankCode);
179 }
180 cashControlDocument.getDocumentHeader().setDocumentDescription(ArConstants.LOCKBOX_DOCUMENT_DESCRIPTION + lockbox.getLockboxNumber());
181
182 // setup the AR header for this CashControl doc
183 LOG.info(" creating AR header for customer: [" + lockbox.getCustomerNumber() + "] and ProcessingOrg: " + sysInfo.getProcessingChartOfAccountCode() + "-" + sysInfo.getProcessingOrganizationCode() + ".");
184 AccountsReceivableDocumentHeader arDocHeader;
185 try {
186 arDocHeader = accountsReceivableDocumentHeaderService.getNewAccountsReceivableDocumentHeader(
187 sysInfo.getProcessingChartOfAccountCode(), sysInfo.getProcessingOrganizationCode());
188 }
189 catch (Exception e) {
190 LOG.error("An Exception was thrown while trying to create a new AccountsReceivableDocumentHeader for the current user: '" + person.getPrincipalName() + "'.", e);
191 throw new RuntimeException("An Exception was thrown while trying to create a new AccountsReceivableDocumentHeader for the current user: '" + person.getPrincipalName() + "'.", e);
192 }
193 arDocHeader.setDocumentNumber(cashControlDocument.getDocumentNumber());
194 arDocHeader.setCustomerNumber(lockbox.getCustomerNumber());
195 cashControlDocument.setAccountsReceivableDocumentHeader(arDocHeader);
196 }
197 // set our control lockbox as the current lockbox and create details.
198 ctrlLockbox = lockbox;
199
200 // write the lockbox detail line to the report
201 writeLockboxRecordLine(pdfdoc, lockbox.getLockboxNumber(), lockbox.getCustomerNumber(), lockbox.getFinancialDocumentReferenceInvoiceNumber(),
202 lockbox.getInvoicePaidOrAppliedAmount(), lockbox.getCustomerPaymentMediumCode(), lockbox.getBankCode());
203
204 // skip zero-dollar-amount lockboxes
205 if (lockbox.getInvoicePaidOrAppliedAmount().isZero()) {
206 LOG.warn(" lockbox has a zero dollar amount, so we're skipping it.");
207 writeSummaryDetailLine(pdfdoc, "ZERO-DOLLAR LOCKBOX - NO FURTHER PROCESSING");
208 deleteProcessedLockboxEntry(lockbox);
209 continue;
210 }
211 if (lockbox.getInvoicePaidOrAppliedAmount().isLessThan(KualiDecimal.ZERO)) {
212 LOG.warn(" lockbox has a negative dollar amount, so we're skipping it.");
213 writeCashControlDetailLine(pdfdoc, lockbox.getInvoicePaidOrAppliedAmount(), "SKIPPED");
214 writeSummaryDetailLine(pdfdoc, "NEGATIVE-DOLLAR LOCKBOX - NO FURTHER PROCESSING - LOCKBOX ENTRY NOT DELETED");
215 continue;
216 }
217
218 // create a new cashcontrol detail
219 CashControlDetail detail = new CashControlDetail();
220 detail.setCustomerNumber(lockbox.getCustomerNumber());
221 detail.setFinancialDocumentLineAmount(lockbox.getInvoicePaidOrAppliedAmount());
222 detail.setCustomerPaymentDate(lockbox.getProcessedInvoiceDate());
223 detail.setCustomerPaymentDescription("Lockbox Remittance " +lockbox.getFinancialDocumentReferenceInvoiceNumber());
224
225 // add it to the document
226 LOG.info(" creating detail for $" + lockbox.getInvoicePaidOrAppliedAmount() + " with invoiceDate: " + lockbox.getProcessedInvoiceDate());
227 try {
228 cashControlDocumentService.addNewCashControlDetail(ArConstants.LOCKBOX_DOCUMENT_DESCRIPTION, cashControlDocument, detail);
229 }
230 catch (Exception e) {
231 LOG.error("A Exception was thrown while trying to create a new CashControl detail.", e);
232 throw new RuntimeException("A Exception was thrown while trying to create a new CashControl detail.", e);
233 }
234
235 // retrieve the docNumber of the generated payapp
236 String payAppDocNumber = detail.getReferenceFinancialDocumentNumber();
237 LOG.info(" new PayAppDoc was created: " + payAppDocNumber + ".");
238
239 String invoiceNumber = lockbox.getFinancialDocumentReferenceInvoiceNumber();
240 LOG.info(" lockbox references invoice number [" + invoiceNumber + "].");
241
242 // before release 3, during dev, sometimes invoice numbers we got from the functional
243 // testing dataset were old FIS style, and not compatible with KFS
244 boolean invoiceNumberNotParsable = false;
245 if (StringUtils.isBlank(invoiceNumber)) {
246 invoiceNumberNotParsable = true;
247 }
248 else {
249 try {
250 Integer.parseInt(invoiceNumber);
251 } catch (Exception e) {
252 invoiceNumberNotParsable = true;
253 }
254 }
255
256 // if thats the case, dont even bother looking for an invoice, just save the CashControl
257 if (invoiceNumberNotParsable) {
258 LOG.info(" invoice number [" + invoiceNumber + "] isnt in expected KFS format, so cannot load the original invoice.");
259 detail.setCustomerPaymentDescription(ArConstants.LOCKBOX_REMITTANCE_FOR_INVALID_INVOICE_NUMBER +lockbox.getFinancialDocumentReferenceInvoiceNumber());
260 try {
261 documentService.saveDocument(cashControlDocument);
262 }
263 catch (Exception e) {
264 LOG.error("A Exception was thrown while trying to save the CashControl document.", e);
265 throw new RuntimeException("A Exception was thrown while trying to save the CashControl document.", e);
266 }
267
268 // write the detail and payapp lines to the report
269 routePayAppWithoutBusinessRules(payAppDocNumber, "CREATED & SAVED by Lockbox batch");
270 writeCashControlDetailLine(pdfdoc, detail.getFinancialDocumentLineAmount(), detail.getCustomerPaymentDescription());
271 writePayAppLine(pdfdoc, detail.getReferenceFinancialDocumentNumber(), "CREATED & SAVED");
272 writeSummaryDetailLine(pdfdoc, "INVOICE NUMBER NOT PARSEABLE");
273 // delete the lockbox now we're done with it
274 deleteProcessedLockboxEntry(lockbox);
275 continue;
276 }
277
278 // check to see if the invoice indicated exists, and if not, then save the CashControl and move on
279 if (!documentService.documentExists(invoiceNumber)) {
280 LOG.info(" invoice number [" + invoiceNumber + "] does not exist in system, so cannot load the original invoice.");
281 detail.setCustomerPaymentDescription(ArConstants.LOCKBOX_REMITTANCE_FOR_INVALID_INVOICE_NUMBER +lockbox.getFinancialDocumentReferenceInvoiceNumber());
282 try {
283 documentService.saveDocument(cashControlDocument);
284 }
285 catch (Exception e) {
286 LOG.error("A Exception was thrown while trying to save the CashControl document.", e);
287 throw new RuntimeException("A Exception was thrown while trying to save the CashControl document.", e);
288 }
289 routePayAppWithoutBusinessRules(payAppDocNumber, "CREATED & SAVED by Lockbox batch");
290 writeCashControlDetailLine(pdfdoc, detail.getFinancialDocumentLineAmount(), detail.getCustomerPaymentDescription());
291 writePayAppLine(pdfdoc, detail.getReferenceFinancialDocumentNumber(), "CREATED & SAVED");
292 writeSummaryDetailLine(pdfdoc, "INVOICE DOESNT EXIST");
293 // delete the lockbox now we're done with it
294 deleteProcessedLockboxEntry(lockbox);
295 continue;
296 }
297
298 // load up the specified invoice from the lockbox
299 LOG.info(" loading invoice number [" + invoiceNumber + "].");
300 CustomerInvoiceDocument customerInvoiceDocument;
301 try {
302 customerInvoiceDocument = (CustomerInvoiceDocument)documentService.getByDocumentHeaderId(invoiceNumber);
303 }
304 catch (Exception e) {
305 LOG.error("A Exception was thrown while trying to load invoice #" + invoiceNumber + ".", e);
306 throw new RuntimeException("A Exception was thrown while trying to load invoice #" + invoiceNumber + ".", e);
307 }
308
309 // if the invoice is already closed, then just save the CashControl and move on
310 writeInvoiceDetailLine(pdfdoc, invoiceNumber, customerInvoiceDocument.isOpenInvoiceIndicator(),
311 customerInvoiceDocument.getCustomer().getCustomerNumber(), customerInvoiceDocument.getOpenAmount());
312 if (!customerInvoiceDocument.isOpenInvoiceIndicator()) {
313 LOG.info(" invoice is already closed, so saving CashControl doc and moving on.");
314 detail.setCustomerPaymentDescription(ArConstants.LOCKBOX_REMITTANCE_FOR_CLOSED_INVOICE_NUMBER +lockbox.getFinancialDocumentReferenceInvoiceNumber());
315 try {
316 documentService.saveDocument(cashControlDocument);
317 }
318 catch (Exception e) {
319 LOG.error("A Exception was thrown while trying to save the CashControl document.", e);
320 throw new RuntimeException("A Exception was thrown while trying to save the CashControl document.", e);
321 }
322 routePayAppWithoutBusinessRules(payAppDocNumber, "CREATED & SAVED by Lockbox batch");
323 writeCashControlDetailLine(pdfdoc, detail.getFinancialDocumentLineAmount(), detail.getCustomerPaymentDescription());
324 writePayAppLine(pdfdoc, detail.getReferenceFinancialDocumentNumber(), "CREATED & SAVED");
325 writeSummaryDetailLine(pdfdoc, "INVOICE ALREADY CLOSED");
326 deleteProcessedLockboxEntry(lockbox);
327 continue;
328 }
329
330 boolean autoApprove = customerInvoiceDocument.getOpenAmount().equals(lockbox.getInvoicePaidOrAppliedAmount());
331 String annotation = "CREATED & SAVED";
332
333 // if the lockbox amount matches the invoice amount, then create, save and approve a PayApp, and then
334 // mark the invoice
335 if (autoApprove){
336 LOG.info(" lockbox amount matches invoice total document amount [" + customerInvoiceDocument.getTotalDollarAmount() + "].");
337 annotation = "CREATED, SAVED, and BLANKET APPROVED";
338
339 // load up the PayApp document that was created
340 LOG.info(" loading the generated PayApp [" + payAppDocNumber + "], so we can route or approve it.");
341 PaymentApplicationDocument payAppDoc;
342 try {
343 payAppDoc = (PaymentApplicationDocument) documentService.getByDocumentHeaderId(payAppDocNumber);
344 }
345 catch (Exception e) {
346 LOG.error("A Exception was thrown while trying to load PayApp #" + payAppDocNumber + ".", e);
347 throw new RuntimeException("A Exception was thrown while trying to load PayApp #" + payAppDocNumber + ".", e);
348 }
349
350 // create paidapplieds on the PayApp doc for all the Invoice details
351 LOG.info(" attempting to create paidApplieds on the PayAppDoc for every detail on the invoice.");
352 payAppDoc = payAppDocService.createInvoicePaidAppliedsForEntireInvoiceDocument(customerInvoiceDocument, payAppDoc);
353 LOG.info(" PayAppDoc has TotalApplied of " + payAppDoc.getTotalApplied() + " for a Control Balance of " + payAppDoc.getTotalFromControl() + ".");
354
355 // Save and approve the payapp doc
356 LOG.info(" attempting to blanketApprove the PayApp Doc.");
357 try {
358
359 documentService.blanketApproveDocument(payAppDoc, "Automatically approved by Lockbox batch job.", null);
360 }
361 catch (Exception e) {
362 LOG.error("A Exception was thrown while trying to blanketApprove PayAppDoc #" + payAppDoc.getDocumentNumber() + ".", e);
363 throw new RuntimeException("A Exception was thrown while trying to blanketApprove PayAppDoc #" + payAppDoc.getDocumentNumber() + ".", e);
364 }
365
366 // write the report details
367 writeCashControlDetailLine(pdfdoc, detail.getFinancialDocumentLineAmount(), detail.getCustomerPaymentDescription());
368 writePayAppLine(pdfdoc, detail.getReferenceFinancialDocumentNumber(), annotation);
369 writeSummaryDetailLine(pdfdoc, "LOCKBOX AMOUNT MATCHES INVOICE OPEN AMOUNT");
370 }
371 else {
372 LOG.info(" lockbox amount does NOT match invoice total document amount [" + customerInvoiceDocument.getTotalDollarAmount() + "].");
373 routePayAppWithoutBusinessRules(payAppDocNumber, "CREATED & SAVED by Lockbox batch");
374
375 // write the report details
376 writeCashControlDetailLine(pdfdoc, detail.getFinancialDocumentLineAmount(), detail.getCustomerPaymentDescription());
377 writePayAppLine(pdfdoc, detail.getReferenceFinancialDocumentNumber(), annotation);
378 if (lockbox.getInvoicePaidOrAppliedAmount().isLessThan(customerInvoiceDocument.getOpenAmount())) {
379 writeSummaryDetailLine(pdfdoc, "LOCKBOX UNDERPAID INVOICE");
380 }
381 else {
382 writeSummaryDetailLine(pdfdoc, "LOCKBOX OVERPAID INVOICE");
383 }
384 }
385
386 // save the cashcontrol, which saves any changes to the details
387 detail.setCustomerPaymentDescription(ArConstants.LOCKBOX_REMITTANCE_FOR_INVOICE_NUMBER +lockbox.getFinancialDocumentReferenceInvoiceNumber());
388 LOG.info(" saving cash control document.");
389 try {
390 documentService.saveDocument(cashControlDocument);
391 }
392 catch (Exception e) {
393 LOG.error("A Exception was thrown while trying to save the CashControl document.", e);
394 throw new RuntimeException("A Exception was thrown while trying to save the CashControl document.", e);
395 }
396
397 // delete the lockbox now we're done with it
398 deleteProcessedLockboxEntry(lockbox);
399 }
400
401 // if we have a cashControlDocument here, then it needs to be routed, its the last one
402 if (cashControlDocument != null) {
403 LOG.info(" routing cash control document.");
404 try {
405 documentService.routeDocument(cashControlDocument, "Routed by Lockbox Batch process.", null);
406 }
407 catch (Exception e) {
408 LOG.error("A Exception was thrown while trying to route the CashControl document.", e);
409 throw new RuntimeException("A Exception was thrown while trying to route the CashControl document.", e);
410 }
411 }
412
413 // if no records were found, write something useful to the report
414 if (!anyRecordsFound) {
415 writeDetailLine(pdfdoc, "NO LOCKBOX RECORDS WERE FOUND");
416 }
417
418 // this annoying all-encompassing try/catch is here to make sure that the report gets
419 // written. without it, if anything goes wrong, the report will end up a zero-byte document.
420 }
421 catch (Exception e) {
422 writeDetailLine(pdfdoc, "AN EXCEPTION OCCURRED:");
423 writeDetailLine(pdfdoc, "");
424 writeDetailLine(pdfdoc, e.getMessage());
425 writeDetailLine(pdfdoc, "");
426 writeExceptionStackTrace(pdfdoc, e);
427 }
428
429 // spool the report
430 pdfdoc.close();
431
432 return true;
433
434 }
435
436 protected void routePayAppWithoutBusinessRules(String payAppDocNumber, String annotation) {
437
438 // load up the PayApp document that was created
439 LOG.info(" loading the generated PayApp [" + payAppDocNumber + "], so we can route or approve it.");
440 PaymentApplicationDocument payAppDoc;
441 try {
442 payAppDoc = (PaymentApplicationDocument) documentService.getByDocumentHeaderId(payAppDocNumber);
443 }
444 catch (Exception e) {
445 LOG.error("A Exception was thrown while trying to load PayApp #" + payAppDocNumber + ".", e);
446 throw new RuntimeException("A Exception was thrown while trying to load PayApp #" + payAppDocNumber + ".", e);
447 }
448
449 // route without business rules
450 LOG.info(" attempting to route without business rules the PayApp Doc.");
451 try {
452 payAppDoc.getDocumentHeader().getWorkflowDocument().routeDocument(annotation);
453 final SearchableAttributeProcessingService searchableAttributeProcessingService = SpringContext.getBean(SearchableAttributeProcessingService.class);
454 searchableAttributeProcessingService.indexDocument(new Long(payAppDoc.getDocumentNumber()));
455 }
456 catch (Exception e) {
457 LOG.error("A Exception was thrown while trying to route (without business rules) PayAppDoc #" + payAppDoc.getDocumentNumber() + ".", e);
458 throw new RuntimeException("A Exception was thrown while trying to route (without business rules) PayAppDoc #" + payAppDoc.getDocumentNumber() + ".", e);
459 }
460 }
461
462 protected void deleteProcessedLockboxEntry(Lockbox lockboxEntry) {
463 Map<String,Object> pkMap = new HashMap<String,Object>();
464 pkMap.put("invoiceSequenceNumber", lockboxEntry.getInvoiceSequenceNumber());
465 boService.deleteMatching(Lockbox.class, pkMap);
466 }
467
468 protected com.lowagie.text.Document getPdfDoc() {
469
470 String reportDropFolder = reportsDirectory + "/" + ArConstants.Lockbox.LOCKBOX_REPORT_SUBFOLDER + "/";
471 String fileName = ArConstants.Lockbox.BATCH_REPORT_BASENAME + "_" +
472 new SimpleDateFormat("yyyyMMdd_HHmmssSSS").format(dateTimeService.getCurrentDate()) + ".pdf";
473
474 // setup the writer
475 File reportFile = new File(reportDropFolder + fileName);
476 FileOutputStream fileOutStream;
477 try {
478 fileOutStream = new FileOutputStream(reportFile);
479 }
480 catch (IOException e) {
481 LOG.error("IOException thrown when trying to open the FileOutputStream.", e);
482 throw new RuntimeException("IOException thrown when trying to open the FileOutputStream.", e);
483 }
484 BufferedOutputStream buffOutStream = new BufferedOutputStream(fileOutStream);
485
486 com.lowagie.text.Document pdfdoc = new com.lowagie.text.Document(PageSize.LETTER, 54, 54, 72, 72);
487 try {
488 PdfWriter.getInstance(pdfdoc, buffOutStream);
489 }
490 catch (DocumentException e) {
491 LOG.error("iText DocumentException thrown when trying to start a new instance of the PdfWriter.", e);
492 throw new RuntimeException("iText DocumentException thrown when trying to start a new instance of the PdfWriter.", e);
493 }
494
495 pdfdoc.open();
496
497 return pdfdoc;
498 }
499
500 protected String rightPad(String valToPad, int sizeToPadTo) {
501 return rightPad(valToPad, sizeToPadTo, " ");
502 }
503
504 protected String rightPad(String valToPad, int sizeToPadTo, String padChar) {
505 if (StringUtils.isBlank(valToPad)) {
506 return StringUtils.repeat(padChar, sizeToPadTo);
507 }
508 if (valToPad.length() >= sizeToPadTo) {
509 return valToPad;
510 }
511 return valToPad + StringUtils.repeat(padChar, sizeToPadTo - valToPad.length());
512 }
513
514 protected void writeBatchGroupSectionTitle(com.lowagie.text.Document pdfDoc, String batchSeqNbr, java.sql.Date procInvDt, String cashControlDocNumber) {
515 Font font = FontFactory.getFont(FontFactory.COURIER, 10, Font.BOLD);
516
517 String lineText = "CASHCTL " + rightPad(cashControlDocNumber, 12) + " " +
518 "BATCH GROUP: " + rightPad(batchSeqNbr, 5) + " " +
519 rightPad((procInvDt == null ? "NONE" : procInvDt.toString()), 35);
520
521 Paragraph paragraph = new Paragraph();
522 paragraph.setAlignment(com.lowagie.text.Element.ALIGN_LEFT);
523 Chunk chunk = new Chunk(lineText, font);
524 chunk.setBackground(Color.LIGHT_GRAY, 5, 5, 5, 5);
525 paragraph.add(chunk);
526
527 // blank line
528 paragraph.add(new Chunk("", font));
529
530 try {
531 pdfDoc.add(paragraph);
532 }
533 catch (DocumentException e) {
534 LOG.error("iText DocumentException thrown when trying to write content.", e);
535 throw new RuntimeException("iText DocumentException thrown when trying to write content.", e);
536 }
537 }
538
539 protected void writeLockboxRecordLine(com.lowagie.text.Document pdfDoc, String lockboxNumber, String customerNumber, String invoiceNumber,
540 KualiDecimal invoiceTotalAmount, String paymentMediumCode, String bankCode) {
541
542 writeDetailLine(pdfDoc, StringUtils.repeat("-", 100));
543
544 StringBuilder sb = new StringBuilder();
545 sb.append(" "); // 3: 1 - 3
546 sb.append("LOCKBOX: " + rightPad(lockboxNumber, 10) + " "); // 20: 4 - 23
547 sb.append("CUST: " + rightPad(customerNumber, 9) + " "); // 15: 24 - 38
548 sb.append("INV: " + rightPad(invoiceNumber, 10) + " "); // 16: 39 - 55
549 sb.append(StringUtils.repeat(" ", 28)); // 28: 56 - 83
550 sb.append("AMT: " + rightPad(invoiceTotalAmount.toString(), 11) + " "); // 17: 84 - 100
551
552 writeDetailLine(pdfDoc, sb.toString());
553 }
554
555 protected void writeInvoiceDetailLine(com.lowagie.text.Document pdfDoc, String invoiceNumber, boolean open, String customerNumber, KualiDecimal openAmount) {
556
557 StringBuilder sb = new StringBuilder();
558 sb.append(" "); // 3: 1 - 3
559 sb.append("INVOICE: " + rightPad(invoiceNumber, 10) + " "); // 20: 4 - 23
560 sb.append("CUST: " + rightPad(customerNumber, 9) + " "); // 15: 24 - 38
561 if (open) {
562 sb.append(rightPad("OPEN", 16) + " "); // 16: 39 - 55
563 }
564 else {
565 sb.append(rightPad("CLOSED", 16) + " "); // 16: 39 - 55
566 }
567 sb.append(StringUtils.repeat(" ", 22)); // 28: 56 - 83
568 sb.append("OPEN AMT: " + rightPad(openAmount.toString(), 11) + " "); // 17: 84 - 100
569
570 writeDetailLine(pdfDoc, sb.toString());
571 }
572
573 protected void writeCashControlDetailLine(com.lowagie.text.Document pdfDoc, KualiDecimal amount, String description) {
574
575 StringBuilder sb = new StringBuilder();
576 sb.append(" "); // 3: 1 - 3
577 sb.append("CASHCTL DTL: " + rightPad(description, 66) + " "); // 80: 4 - 83
578 sb.append("AMT: " + rightPad(amount.toString(), 11) + " "); // 17: 84 - 100
579
580 writeDetailLine(pdfDoc, sb.toString());
581 }
582
583 protected void writeSummaryDetailLine(com.lowagie.text.Document pdfDoc, String summary) {
584 writeDetailLine(pdfDoc, " " + summary);
585 }
586
587 protected void writePayAppLine(com.lowagie.text.Document pdfDoc, String payAppDocNbr, String description) {
588
589 StringBuilder sb = new StringBuilder();
590 sb.append(" "); // 3: 1 - 3
591 sb.append("PAYAPP DOC NBR: " + rightPad(payAppDocNbr, 12) + " "); // 29: 4 - 32
592 sb.append("ACTION: " + description); // 40: 33 - 72
593
594 writeDetailLine(pdfDoc, sb.toString());
595 }
596
597 protected void writeExceptionStackTrace(com.lowagie.text.Document pdfDoc, Exception e) {
598 ByteArrayOutputStream outStream = new ByteArrayOutputStream();
599 PrintWriter printWriter = new PrintWriter(outStream, true);
600
601 e.printStackTrace(printWriter);
602 printWriter.flush();
603 writeDetailLine(pdfDoc, outStream.toString());
604 }
605
606 protected void writeDetailLine(com.lowagie.text.Document pdfDoc, String detailLineText) {
607 Font font = FontFactory.getFont(FontFactory.COURIER, 8, Font.NORMAL);
608
609 Paragraph paragraph = new Paragraph();
610 paragraph.setAlignment(com.lowagie.text.Element.ALIGN_LEFT);
611 paragraph.add(new Chunk(detailLineText, font));
612
613 try {
614 pdfDoc.add(paragraph);
615 }
616 catch (DocumentException e) {
617 LOG.error("iText DocumentException thrown when trying to write content.", e);
618 throw new RuntimeException("iText DocumentException thrown when trying to write content.", e);
619 }
620 }
621
622 public Long getMaxLockboxSequenceNumber() {
623 return lockboxDao.getMaxLockboxSequenceNumber();
624 }
625
626 public LockboxDao getLockboxDao() {
627 return lockboxDao;
628 }
629
630 public void setLockboxDao(LockboxDao lockboxDao) {
631 this.lockboxDao = lockboxDao;
632 }
633
634 public SystemInformationService getSystemInformationService() {
635 return systemInformationService;
636 }
637
638 public void setSystemInformationService(SystemInformationService systemInformationService) {
639 this.systemInformationService = systemInformationService;
640 }
641
642 public AccountsReceivableDocumentHeaderService getAccountsReceivableDocumentHeaderService() {
643 return accountsReceivableDocumentHeaderService;
644 }
645
646 public void setAccountsReceivableDocumentHeaderService(AccountsReceivableDocumentHeaderService accountsReceivableDocumentHeaderService) {
647 this.accountsReceivableDocumentHeaderService = accountsReceivableDocumentHeaderService;
648 }
649
650 public void setPaymentApplicationDocumentService(PaymentApplicationDocumentService paymentApplicationDocumentService) {
651 this.payAppDocService = paymentApplicationDocumentService;
652 }
653
654 /**
655 * @return Returns the personService.
656 */
657 protected PersonService<Person> getPersonService() {
658 if(personService==null)
659 personService = SpringContext.getBean(PersonService.class);
660 return personService;
661 }
662
663 /**
664 * Gets the documentService attribute.
665 * @return Returns the documentService.
666 */
667 public DocumentService getDocumentService() {
668 return documentService;
669 }
670
671 /**
672 * Sets the documentService attribute value.
673 * @param documentService The documentService to set.
674 */
675 public void setDocumentService(DocumentService documentService) {
676 this.documentService = documentService;
677 }
678
679 /**
680 * Sets the dataDictionaryService attribute value.
681 * @param dataDictionaryService The dataDictionaryService to set.
682 */
683 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
684 this.dataDictionaryService = dataDictionaryService;
685 }
686
687 public void setDateTimeService(DateTimeService dateTimeService) {
688 this.dateTimeService = dateTimeService;
689 }
690
691 public CashControlDocumentService getCashControlDocumentService() {
692 return cashControlDocumentService;
693 }
694
695 public void setCashControlDocumentService(CashControlDocumentService cashControlDocumentService) {
696 this.cashControlDocumentService = cashControlDocumentService;
697 }
698
699 public void setReportsDirectory(String reportsDirectory) {
700 this.reportsDirectory = reportsDirectory;
701 }
702
703 public void setBoService(BusinessObjectService boService) {
704 this.boService = boService;
705 }
706
707 }