1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.ole.gl.batch.service.impl;
17  
18  import java.text.MessageFormat;
19  import java.util.ArrayList;
20  import java.util.Formattable;
21  import java.util.Formatter;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.commons.lang.StringUtils;
30  import org.kuali.ole.gl.batch.CollectorBatch;
31  import org.kuali.ole.gl.batch.CollectorStep;
32  import org.kuali.ole.gl.batch.service.CollectorReportService;
33  import org.kuali.ole.gl.businessobject.DemergerReportData;
34  import org.kuali.ole.gl.businessobject.OriginEntryFull;
35  import org.kuali.ole.gl.businessobject.Transaction;
36  import org.kuali.ole.gl.report.CollectorReportData;
37  import org.kuali.ole.gl.report.LedgerSummaryReport;
38  import org.kuali.ole.gl.report.PreScrubberReport;
39  import org.kuali.ole.gl.report.Summary;
40  import org.kuali.ole.gl.service.PreScrubberService;
41  import org.kuali.ole.gl.service.ScrubberReportData;
42  import org.kuali.ole.sys.OLEConstants;
43  import org.kuali.ole.sys.OLEConstants.SystemGroupParameterNames;
44  import org.kuali.ole.sys.OLEKeyConstants;
45  import org.kuali.ole.sys.Message;
46  import org.kuali.ole.sys.service.ReportWriterService;
47  import org.kuali.rice.core.api.config.property.ConfigurationService;
48  import org.kuali.rice.core.api.datetime.DateTimeService;
49  import org.kuali.rice.core.api.mail.MailMessage;
50  import org.kuali.rice.core.api.util.type.KualiDecimal;
51  import org.kuali.rice.core.web.format.CurrencyFormatter;
52  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
53  import org.kuali.rice.krad.service.MailService;
54  import org.kuali.rice.krad.util.ErrorMessage;
55  import org.kuali.rice.krad.util.MessageMap;
56  
57  
58  
59  
60  public class CollectorReportServiceImpl implements CollectorReportService {
61      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(CollectorReportServiceImpl.class);
62  
63      protected DateTimeService dateTimeService;
64      protected ParameterService parameterService;
65      protected ConfigurationService configurationService;
66      protected MailService mailService;
67      protected PreScrubberService preScrubberService;
68      protected ReportWriterService collectorReportWriterService;
69  
70      
71  
72  
73  
74  
75  
76      @Override
77      public void sendEmails(CollectorReportData collectorReportData) {
78          
79          Iterator<CollectorBatch> batchIter = collectorReportData.getAddedBatches();
80          while (batchIter.hasNext()) {
81              CollectorBatch batch = batchIter.next();
82              sendValidationEmail(batch, collectorReportData);
83              sendDemergerEmail(batch, collectorReportData);
84          }
85          
86          sendEmailSendFailureNotice(collectorReportData);
87      }
88  
89      
90  
91  
92  
93  
94  
95      @Override
96      public void generateCollectorRunReports(CollectorReportData collectorReportData) {
97          appendCollectorHeaderInformation(collectorReportData);
98          appendPreScrubberReport(collectorReportData);
99          appendScrubberReport(collectorReportData);
100         appendDemergerReport(collectorReportData);
101         appendDeletedOriginEntryAndDetailReport(collectorReportData);
102         appendDetailChangedAccountReport(collectorReportData);
103         appendLedgerReport(collectorReportData);
104     }
105 
106     
107 
108 
109 
110 
111     protected void appendCollectorHeaderInformation(CollectorReportData collectorReportData) {
112         Iterator<CollectorBatch> batchIter = collectorReportData.getAddedBatches();
113         OriginEntryTotals aggregateOriginEntryTotals = new OriginEntryTotals();
114         int aggregateTotalRecordsCountFromTrailer = 0;
115         int aggregateNumInputDetails = 0;
116         int aggregateNumSavedDetails = 0;
117 
118         if (!collectorReportData.getAllUnparsableFileNames().isEmpty()) {
119             collectorReportWriterService.writeFormattedMessageLine("The following files could not be parsed:\n\n");
120             for (String unparsableFileName : collectorReportData.getAllUnparsableFileNames()) {
121                 List<String> batchErrors = translateErrorsFromMessageMap(collectorReportData.getMessageMapForFileName(unparsableFileName));
122                 collectorReportWriterService.writeFormattedMessageLine("        " + unparsableFileName + "\n");
123                 for (String errorMessage : batchErrors) {
124                     collectorReportWriterService.writeFormattedMessageLine("        - ERROR MESSAGE: " + errorMessage);
125                 }
126             }
127         }
128 
129         while (batchIter.hasNext()) {
130             CollectorBatch batch = batchIter.next();
131             StringBuilder buf = new StringBuilder();
132 
133             OriginEntryTotals batchOriginEntryTotals = batch.getOriginEntryTotals();
134             appendHeaderInformation(buf, batch, collectorReportData);
135             appendTotalsInformation(buf, batch);
136 
137             List<String> errorMessages = translateErrorsFromMessageMap(batch.getMessageMap());
138 
139             aggregateTotalRecordsCountFromTrailer += batch.getTotalRecords();
140 
141             
142             if (collectorReportData.isBatchValid(batch)) {
143 
144                 if (batchOriginEntryTotals != null) {
145                     aggregateOriginEntryTotals.incorporateTotals(batchOriginEntryTotals);
146                 }
147 
148                 Integer batchNumInputDetails = collectorReportData.getNumInputDetails(batch);
149                 if (batchNumInputDetails != null) {
150                     aggregateNumInputDetails += batchNumInputDetails;
151                 }
152 
153                 Integer batchNumSavedDetails = collectorReportData.getNumSavedDetails(batch);
154                 if (batchNumSavedDetails != null) {
155                     aggregateNumSavedDetails += batchNumSavedDetails;
156                 }
157             }
158 
159             collectorReportWriterService.writeFormattedMessageLine("Header  *********************************************************************");
160             collectorReportWriterService.writeMultipleFormattedMessageLines(buf.toString());
161 
162             String validationErrors = getValidationStatus(errorMessages, false, 15);
163             if (StringUtils.isNotBlank(validationErrors)) {
164                 collectorReportWriterService.writeMultipleFormattedMessageLines(validationErrors);
165             }
166         }
167 
168         collectorReportWriterService.writeNewLines(2);
169         collectorReportWriterService.writeFormattedMessageLine("***** Totals for Creation of GLE Data  *****");
170         collectorReportWriterService.writeFormattedMessageLine("      Total Records Read      %09d", aggregateTotalRecordsCountFromTrailer);
171         collectorReportWriterService.writeFormattedMessageLine("      Total Groups Read       %09d", collectorReportData.getNumPersistedBatches());
172         collectorReportWriterService.writeFormattedMessageLine("      Total Groups Bypassed   %09d", collectorReportData.getNumNotPersistedBatches());
173         int totalRecordsBypassed = collectorReportData.getNumNotPersistedOriginEntryRecords() + collectorReportData.getNumNotPersistedCollectorDetailRecords();
174         collectorReportWriterService.writeFormattedMessageLine("      Total Records Bypassed  %09d", totalRecordsBypassed);
175         collectorReportWriterService.writeFormattedMessageLine("      Total WWW Records Out   %09d", aggregateNumInputDetails);
176         int aggregateOriginEntryCountFromParsedData = aggregateOriginEntryTotals.getNumCreditEntries() + aggregateOriginEntryTotals.getNumDebitEntries() + aggregateOriginEntryTotals.getNumOtherEntries();
177         collectorReportWriterService.writeFormattedMessageLine("      Total GLE Records Out   %09d", aggregateOriginEntryCountFromParsedData);
178         collectorReportWriterService.writeFormattedMessageLine("      Total GLE Debits        %19s", new KualiDecimalFormatter(aggregateOriginEntryTotals.getDebitAmount()));
179         collectorReportWriterService.writeFormattedMessageLine("      Debit Count             %09d", aggregateOriginEntryTotals.getNumDebitEntries());
180         collectorReportWriterService.writeFormattedMessageLine("      Total GLE Credits       %19s", new KualiDecimalFormatter(aggregateOriginEntryTotals.getCreditAmount()));
181         collectorReportWriterService.writeFormattedMessageLine("      Debit Count             %09d", aggregateOriginEntryTotals.getNumCreditEntries());
182         collectorReportWriterService.writeFormattedMessageLine("      Total GLE Not C or D    %19s", new KualiDecimalFormatter(aggregateOriginEntryTotals.getOtherAmount()));
183         collectorReportWriterService.writeFormattedMessageLine("      Not C or D Count        %09d", aggregateOriginEntryTotals.getNumOtherEntries());
184         collectorReportWriterService.writeNewLines(1);
185         collectorReportWriterService.writeFormattedMessageLine("Inserted %d detail records into gl_id_bill_t", aggregateNumSavedDetails);
186     }
187 
188     
189 
190 
191 
192 
193 
194     protected void appendHeaderInformation(StringBuilder buf, CollectorBatch batch, CollectorReportData collectorReportData) {
195         
196         String emailBatchStatus = collectorReportData.getEmailSendingStatus().get(batch.getBatchName());
197         
198         buf.append("\n        Chart: ").append(batch.getChartOfAccountsCode()).append("\n");
199         buf.append("        Org: ").append(batch.getOrganizationCode()).append("\n");
200         buf.append("        Campus: ").append(batch.getCampusCode()).append("\n");
201         buf.append("        Department: ").append(batch.getDepartmentName()).append("\n");
202         buf.append("        Mailing Address: ").append(batch.getMailingAddress()).append("\n");
203         buf.append("        Contact: ").append(batch.getPersonUserID()).append("\n");
204         buf.append("        Email: ").append(batch.getEmailAddress());
205         if (StringUtils.isNotEmpty(emailBatchStatus)){
206             String displayStatus = StringUtils.containsIgnoreCase(emailBatchStatus, "ERROR")? "**Email Failure" : "**Email Success";
207             buf.append(" ( " + displayStatus + " )").append("\n");
208         }else{
209             buf.append("\n");
210         }
211         buf.append("        Transmission Date: ").append(batch.getTransmissionDate()).append("\n\n");
212     }
213 
214     
215 
216 
217 
218 
219 
220 
221     protected void appendTotalsInformation(StringBuilder buf, CollectorBatch batch) {
222         OriginEntryTotals totals = batch.getOriginEntryTotals();
223         if (totals == null) {
224             buf.append("        Totals are unavailable for this batch.\n");
225         }
226         else {
227             
228             appendAmountCountLine(buf, "Group Credits     = ", Integer.toString(totals.getNumCreditEntries()), totals.getCreditAmount());
229             appendAmountCountLine(buf, "Group Debits      = ", Integer.toString(totals.getNumDebitEntries()), totals.getDebitAmount());
230             appendAmountCountLine(buf, "Group Not C/D     = ", Integer.toString(totals.getNumOtherEntries()), totals.getOtherAmount());
231             appendAmountCountLine(buf, "Valid Group Count = ", batch.getTotalRecords().toString(), batch.getTotalAmount());
232         }
233     }
234 
235     
236 
237 
238 
239 
240 
241 
242 
243     protected void appendAmountCountLine(StringBuilder buf, String countTitle, String count, KualiDecimal amount) {
244         appendPaddingString(buf, ' ', countTitle.length(), 35);
245         buf.append(countTitle);
246 
247         appendPaddingString(buf, '0', count.length(), 5);
248         buf.append(count);
249 
250         if (amount == null) {
251             buf.append(StringUtils.leftPad("N/A", 21));
252         }
253         else {
254             Map<String, String> settings = new HashMap<String, String>();
255             settings.put(CurrencyFormatter.SHOW_SYMBOL, Boolean.TRUE.toString());
256             org.kuali.rice.core.web.format.Formatter f = org.kuali.rice.core.web.format.Formatter.getFormatter(KualiDecimal.class, settings);
257             String amountString = (String) f.format(amount);
258             appendPaddingString(buf, ' ', amountString.length(), 21);
259             buf.append(amountString);
260         }
261         
262         buf.append("\n");
263 
264     }
265 
266     
267 
268 
269 
270 
271 
272 
273 
274 
275     protected StringBuilder appendPaddingString(StringBuilder buf, char padCharacter, int valueLength, int desiredLength) {
276         for (int i = valueLength; i < desiredLength; i++) {
277             buf.append(padCharacter);
278         }
279         return buf;
280     }
281 
282     protected void appendPreScrubberReport(CollectorReportData collectorReportData) {
283         if (preScrubberService.deriveChartOfAccountsCodeIfSpaces()) {
284             collectorReportWriterService.pageBreak();
285             collectorReportWriterService.writeSubTitle("Collector Pre-Scrubber Report");
286             new PreScrubberReport().generateReport(collectorReportData.getPreScrubberReportData(), collectorReportWriterService);
287         }
288     }
289     
290     
291 
292 
293 
294 
295     protected void appendScrubberReport(CollectorReportData collectorReportData) {
296         Iterator<CollectorBatch> batchIter = collectorReportData.getAddedBatches();
297         ScrubberReportData aggregateScrubberReportData = new ScrubberReportData();
298         Map<Transaction, List<Message>> aggregateScrubberErrors = new LinkedHashMap<Transaction, List<Message>>();
299 
300         collectorReportWriterService.pageBreak();
301         
302         while (batchIter.hasNext()) {
303             CollectorBatch batch = batchIter.next();
304 
305             ScrubberReportData batchScrubberReportData = collectorReportData.getScrubberReportData(batch);
306             if (batchScrubberReportData != null) {
307                 
308                 aggregateScrubberReportData.incorporateReportData(batchScrubberReportData);
309             }
310 
311             Map<Transaction, List<Message>> batchScrubberReportErrors = collectorReportData.getBatchOriginEntryScrubberErrors(batch);
312             if (batchScrubberReportErrors != null) {
313                 
314                 aggregateScrubberErrors.putAll(batchScrubberReportErrors);
315             }
316         }
317 
318         List<Transaction> transactions = new ArrayList<Transaction>(aggregateScrubberErrors.keySet());
319         for (Transaction errorTrans : aggregateScrubberErrors.keySet()) {
320             List<Message> errors = aggregateScrubberErrors.get(errorTrans);
321             collectorReportWriterService.writeError(errorTrans, errors);
322         }
323         collectorReportWriterService.writeStatisticLine("UNSCRUBBED RECORDS READ                     %,9d", aggregateScrubberReportData.getNumberOfUnscrubbedRecordsRead());
324         collectorReportWriterService.writeStatisticLine("SCRUBBED RECORDS WRITTEN                    %,9d", aggregateScrubberReportData.getNumberOfScrubbedRecordsWritten());
325         collectorReportWriterService.writeStatisticLine("ERROR RECORDS WRITTEN                       %,9d", aggregateScrubberReportData.getNumberOfErrorRecordsWritten());
326         collectorReportWriterService.writeStatisticLine("TOTAL OUTPUT RECORDS WRITTEN                %,9d", aggregateScrubberReportData.getTotalNumberOfRecordsWritten());
327         collectorReportWriterService.writeStatisticLine("EXPIRED ACCOUNTS FOUND                      %,9d", aggregateScrubberReportData.getNumberOfExpiredAccountsFound());
328     }
329 
330     
331 
332 
333 
334 
335 
336     protected void appendDemergerReport(CollectorReportData collectorReportData) {
337         Iterator<CollectorBatch> batchIter = collectorReportData.getAddedBatches();
338         DemergerReportData aggregateDemergerReportData = new DemergerReportData();
339         ScrubberReportData aggregateScrubberReportData = new ScrubberReportData();
340 
341         while (batchIter.hasNext()) {
342             CollectorBatch batch = batchIter.next();
343             DemergerReportData batchDemergerReportData = collectorReportData.getDemergerReportData(batch);
344             if (batchDemergerReportData != null) {
345                 aggregateDemergerReportData.incorporateReportData(batchDemergerReportData);
346             }
347         }
348 
349         collectorReportWriterService.pageBreak();
350         collectorReportWriterService.writeStatisticLine("ERROR RECORDS READ                          %,9d", aggregateDemergerReportData.getErrorTransactionsRead());
351         collectorReportWriterService.writeStatisticLine("VALID RECORDS READ                          %,9d", aggregateDemergerReportData.getValidTransactionsRead());
352         collectorReportWriterService.writeStatisticLine("ERROR RECORDS REMOVED FROM PROCESSING       %,9d", aggregateDemergerReportData.getErrorTransactionsSaved());
353         collectorReportWriterService.writeStatisticLine("VALID RECORDS ENTERED INTO ORIGIN ENTRY     %,9d", aggregateDemergerReportData.getValidTransactionsSaved());
354     }
355 
356     
357 
358 
359 
360 
361 
362     protected void appendDeletedOriginEntryAndDetailReport(CollectorReportData collectorReportData) {
363         
364         Iterator<CollectorBatch> batchIter = collectorReportData.getAddedBatches();
365         int aggregateNumDetailsDeleted = 0;
366 
367         StringBuilder buf = new StringBuilder();
368 
369         collectorReportWriterService.pageBreak();
370         collectorReportWriterService.writeFormattedMessageLine("ID-Billing detail data matched with GLE errors to remove documents with errors");
371         while (batchIter.hasNext()) {
372             CollectorBatch batch = batchIter.next();
373 
374             Integer batchNumDetailsDeleted = collectorReportData.getNumDetailDeleted(batch);
375             if (batchNumDetailsDeleted != null) {
376                 aggregateNumDetailsDeleted += batchNumDetailsDeleted.intValue();
377             }
378         }
379         collectorReportWriterService.writeFormattedMessageLine("Total-Recs-Bypassed  %d", aggregateNumDetailsDeleted);
380 
381         batchIter = collectorReportData.getAddedBatches();
382         int aggregateTransactionCount = 0;
383         KualiDecimal aggregateDebitAmount = KualiDecimal.ZERO;
384         while (batchIter.hasNext()) {
385             CollectorBatch batch = batchIter.next();
386 
387             Map<DocumentGroupData, OriginEntryTotals> inputEntryTotals = collectorReportData.getTotalsOnInputOriginEntriesAssociatedWithErrorGroup(batch);
388             if (inputEntryTotals != null) {
389                 for (Map.Entry<DocumentGroupData, OriginEntryTotals> errorDocumentGroupEntry : inputEntryTotals.entrySet()) {
390                     
391                     
392                     
393 
394                     collectorReportWriterService.writeFormattedMessageLine("Message sent to %-40s for Document %s", batch.getEmailAddress(), errorDocumentGroupEntry.getKey().getDocumentNumber());
395                     int documentTransactionCount = errorDocumentGroupEntry.getValue().getNumCreditEntries() + errorDocumentGroupEntry.getValue().getNumDebitEntries() + errorDocumentGroupEntry.getValue().getNumOtherEntries();
396                     aggregateTransactionCount += documentTransactionCount;
397                     aggregateDebitAmount = aggregateDebitAmount.add(errorDocumentGroupEntry.getValue().getDebitAmount());
398                     collectorReportWriterService.writeFormattedMessageLine("Total Transactions %d for Total Debit Amount %s", documentTransactionCount, new KualiDecimalFormatter(errorDocumentGroupEntry.getValue().getDebitAmount()));
399                 }
400             }
401         }
402         collectorReportWriterService.writeFormattedMessageLine("Total Error Records %d", aggregateTransactionCount);
403         collectorReportWriterService.writeFormattedMessageLine("Total Debit Dollars %s", new KualiDecimalFormatter(aggregateDebitAmount));
404     }
405 
406     
407 
408 
409 
410 
411 
412     protected void appendDetailChangedAccountReport(CollectorReportData collectorReportData) {
413         StringBuilder buf = new StringBuilder();
414 
415         collectorReportWriterService.writeNewLines(3);
416         collectorReportWriterService.writeFormattedMessageLine("ID-Billing Detail Records with Account Numbers Changed Due to Change of Corresponding GLE Data");
417         Iterator<CollectorBatch> batchIter = collectorReportData.getAddedBatches();
418         int aggregateNumDetailAccountValuesChanged = 0;
419         while (batchIter.hasNext()) {
420             CollectorBatch batch = batchIter.next();
421 
422             Integer batchNumDetailAccountValuesChanged = collectorReportData.getNumDetailAccountValuesChanged(batch);
423             if (batchNumDetailAccountValuesChanged != null) {
424                 aggregateNumDetailAccountValuesChanged += batchNumDetailAccountValuesChanged;
425             }
426         }
427         collectorReportWriterService.writeFormattedMessageLine("Tot-Recs-Changed %d", aggregateNumDetailAccountValuesChanged);
428     }
429 
430     
431 
432 
433 
434 
435     public void setDateTimeService(DateTimeService dateTimeService) {
436         this.dateTimeService = dateTimeService;
437     }
438 
439     
440 
441 
442 
443 
444 
445 
446     protected List<Summary> buildDemergerReportSummary(ScrubberReportData scrubberReportData, DemergerReportData demergerReport) {
447         List<Summary> reportSummary = new ArrayList<Summary>();
448         reportSummary.add(new Summary(1, "ERROR RECORDS READ", new Integer(scrubberReportData.getNumberOfErrorRecordsWritten())));
449         reportSummary.add(new Summary(2, "VALID RECORDS READ", new Integer(scrubberReportData.getNumberOfScrubbedRecordsWritten())));
450         reportSummary.add(new Summary(3, "ERROR RECORDS REMOVED FROM PROCESSING", new Integer(demergerReport.getErrorTransactionsSaved())));
451         reportSummary.add(new Summary(4, "VALID RECORDS ENTERED INTO ORIGIN ENTRY", new Integer(demergerReport.getValidTransactionsSaved())));
452 
453         return reportSummary;
454     }
455 
456     
457 
458 
459 
460 
461 
462     protected void appendLedgerReport(CollectorReportData collectorReportData) {
463         collectorReportWriterService.pageBreak();
464         collectorReportWriterService.writeSubTitle("GENERAL LEDGER INPUT TRANSACTIONS FROM COLLECTOR");
465         collectorReportWriterService.writeNewLines(1);
466 
467         LedgerSummaryReport ledgerSummaryReport = collectorReportData.getLedgerSummaryReport();
468         ledgerSummaryReport.writeReport(collectorReportWriterService);
469     }
470 
471     
472 
473 
474 
475 
476     protected List<String> translateErrorsFromMessageMap(MessageMap messageMap) {
477         List<String> collectorErrors = new ArrayList<String>();
478 
479         for (Iterator<String> iter = messageMap.getPropertiesWithErrors().iterator(); iter.hasNext();) {
480             String errorKey = iter.next();
481 
482             for (Iterator<ErrorMessage> iter2 = messageMap.getMessages(errorKey).iterator(); iter2.hasNext();) {
483                 ErrorMessage errorMessage = (ErrorMessage) iter2.next();
484                 String messageText = configurationService.getPropertyValueAsString(errorMessage.getErrorKey());
485                 collectorErrors.add(MessageFormat.format(messageText, (Object[]) errorMessage.getMessageParameters()));
486             }
487         }
488 
489         return collectorErrors;
490     }
491 
492     
493 
494 
495 
496 
497     protected void sendValidationEmail(CollectorBatch batch, CollectorReportData collectorReportData) {
498         if (StringUtils.isBlank(batch.getEmailAddress())) {
499             LOG.error("Email not sent because email is blank, batch name " + batch.getBatchName());
500             return;
501         }
502         MessageMap messageMap = batch.getMessageMap();
503         List<String> errorMessages = translateErrorsFromMessageMap(messageMap);
504 
505         LOG.debug("sendValidationEmail() starting");
506         MailMessage message = new MailMessage();
507 
508         String returnAddress = parameterService.getParameterValueAsString(OLEConstants.ParameterNamespaces.GL, "Batch", OLEConstants.FROM_EMAIL_ADDRESS_PARM_NM);
509         if(StringUtils.isEmpty(returnAddress)) {
510             returnAddress = mailService.getBatchMailingList();
511         }
512         message.setFromAddress(returnAddress);
513 
514         String subject = parameterService.getParameterValueAsString(CollectorStep.class, SystemGroupParameterNames.COLLECTOR_VALIDATOR_EMAIL_SUBJECT_PARAMETER_NAME);
515         
516         if (errorMessages.size() >0){
517             subject = parameterService.getParameterValueAsString(CollectorStep.class, SystemGroupParameterNames.COLLECTOR_VALIDATOR_ERROR_EMAIL_SUBJECT_PARAMETER_NAME);
518         }
519         message.setSubject(subject);
520 
521         String body = createValidationMessageBody(errorMessages, batch, collectorReportData);
522         message.setMessage(body);
523         message.addToAddress(batch.getEmailAddress());
524 
525         try {
526             mailService.sendMessage(message);
527 
528             String notificationMessage = configurationService.getPropertyValueAsString(OLEKeyConstants.Collector.NOTIFICATION_EMAIL_SENT);
529             String formattedMessage = MessageFormat.format(notificationMessage, new Object[] { batch.getEmailAddress() });
530             collectorReportData.setEmailSendingStatusForParsedBatch(batch, formattedMessage);
531         }
532         catch (Exception e) {
533             LOG.error("sendErrorEmail() Invalid email address. Message not sent", e);
534             String errorMessage = configurationService.getPropertyValueAsString(OLEKeyConstants.Collector.EMAIL_SEND_ERROR);
535             String formattedMessage = MessageFormat.format(errorMessage, new Object[] { batch.getEmailAddress() });
536             collectorReportData.setEmailSendingStatusForParsedBatch(batch, formattedMessage);
537         }
538     }
539 
540     
541 
542 
543 
544 
545 
546     protected void sendDemergerEmail(CollectorBatch batch, CollectorReportData collectorReportData) {
547         if (StringUtils.isBlank(batch.getEmailAddress())) {
548             LOG.error("Email not sent because email is blank, batch name " + batch.getBatchName());
549             return;
550         }
551         LOG.debug("sendDemergerEmail() starting");
552         String body = createDemergerMessageBody(batch, collectorReportData);
553         if (body == null) {
554             
555             return;
556         }
557         MailMessage message = new MailMessage();
558 
559         String returnAddress = parameterService.getParameterValueAsString(OLEConstants.ParameterNamespaces.GL, "Batch", OLEConstants.FROM_EMAIL_ADDRESS_PARM_NM);
560         if(StringUtils.isEmpty(returnAddress)) {
561             returnAddress = mailService.getBatchMailingList();
562         }
563         message.setFromAddress(returnAddress);
564 
565         String subject = parameterService.getParameterValueAsString(CollectorStep.class, SystemGroupParameterNames.COLLECTOR_DEMERGER_EMAIL_SUBJECT_PARAMETER_NAME);
566         String productionEnvironmentCode = configurationService.getPropertyValueAsString(OLEConstants.PROD_ENVIRONMENT_CODE_KEY);
567         String environmentCode = configurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
568         if (!StringUtils.equals(productionEnvironmentCode, environmentCode)) {
569             subject = environmentCode + ": " + subject;
570         }
571         message.setSubject(subject);
572 
573         message.setMessage(body);
574         message.addToAddress(batch.getEmailAddress());
575 
576         try {
577             mailService.sendMessage(message);
578 
579             String notificationMessage = configurationService.getPropertyValueAsString(OLEKeyConstants.Collector.NOTIFICATION_EMAIL_SENT);
580             String formattedMessage = MessageFormat.format(notificationMessage, new Object[] { batch.getEmailAddress() });
581             collectorReportData.setEmailSendingStatusForParsedBatch(batch, formattedMessage);
582         }
583         catch (Exception e) {
584             LOG.error("sendErrorEmail() Invalid email address. Message not sent", e);
585             String errorMessage = configurationService.getPropertyValueAsString(OLEKeyConstants.Collector.EMAIL_SEND_ERROR);
586             String formattedMessage = MessageFormat.format(errorMessage, new Object[] { batch.getEmailAddress() });
587             collectorReportData.setEmailSendingStatusForParsedBatch(batch, formattedMessage);
588         }
589     }
590     
591     
592 
593 
594 
595 
596     protected void sendEmailSendFailureNotice(CollectorReportData collectorReportData) {
597         MailMessage message = new MailMessage();
598 
599         String returnAddress = parameterService.getParameterValueAsString(OLEConstants.ParameterNamespaces.GL, "Batch", OLEConstants.FROM_EMAIL_ADDRESS_PARM_NM);
600         if(StringUtils.isEmpty(returnAddress)) {
601             returnAddress = mailService.getBatchMailingList();
602         }
603         message.setFromAddress(returnAddress);
604 
605         String subject = configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_COLLECTOR_EMAILSEND_NOTIFICATION_SUBJECT);
606         String productionEnvironmentCode = configurationService.getPropertyValueAsString(OLEConstants.PROD_ENVIRONMENT_CODE_KEY);
607         String environmentCode = configurationService.getPropertyValueAsString(OLEConstants.ENVIRONMENT_KEY);
608         if (!StringUtils.equals(productionEnvironmentCode, environmentCode)) {
609             subject = environmentCode + ": " + subject;
610         }
611         message.setSubject(subject);
612 
613         boolean hasEmailSendErrors = false;
614 
615         String body = configurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_COLLECTOR_EMAILSEND_NOTIFICATION_BODY);
616         for (String batchId : collectorReportData.getEmailSendingStatus().keySet()) {
617             String emailStatus = collectorReportData.getEmailSendingStatus().get(batchId);
618             if (StringUtils.containsIgnoreCase(emailStatus, "error")) {
619                 body += "Batch: " + batchId + " - " + emailStatus + "\n";
620                 hasEmailSendErrors = true;
621             }
622         }
623         message.setMessage(body);
624 
625         message.addToAddress(mailService.getBatchMailingList());
626 
627         try {
628             if (hasEmailSendErrors) {
629                 mailService.sendMessage(message);
630                 LOG.info("Email failure notice has been sent to : " + message.getToAddresses() );
631             }
632         }
633         catch (Exception e) {
634             LOG.error("sendErrorEmail() Invalid email address. Message not sent", e);
635         }
636     }
637 
638     
639 
640 
641 
642 
643 
644 
645 
646     protected String createValidationMessageBody(List<String> errorMessages, CollectorBatch batch, CollectorReportData collectorReportData) {
647         StringBuilder body = new StringBuilder();
648 
649         MessageMap fileMessageMap = batch.getMessageMap();
650 
651         body.append("Header Information:\n\n");
652         if (!fileMessageMap.containsMessageKey(OLEKeyConstants.ERROR_BATCH_UPLOAD_PARSING_XML)) {
653             appendHeaderInformation(body, batch, collectorReportData);
654             appendTotalsInformation(body, batch);
655             appendValidationStatus(body, errorMessages, true, 0);
656         }
657 
658         return body.toString();
659     }
660 
661     
662 
663 
664 
665 
666 
667 
668 
669     protected String getValidationStatus(List<String> errorMessages, boolean notifyIfSuccessful, int numLeftPaddingSpaces) {
670         StringBuilder buf = new StringBuilder();
671         appendValidationStatus(buf, errorMessages, notifyIfSuccessful, numLeftPaddingSpaces);
672         return buf.toString();
673     }
674 
675     
676 
677 
678 
679 
680 
681 
682 
683     protected void appendValidationStatus(StringBuilder buf, List<String> errorMessages, boolean notifyIfSuccessful, int numLeftPaddingSpaces) {
684         String padding = StringUtils.leftPad("", numLeftPaddingSpaces, ' ');
685 
686         if (notifyIfSuccessful || !errorMessages.isEmpty()) {
687             buf.append("\n").append(padding).append("Reported Errors:\n");
688         }
689 
690         
691         if (errorMessages.isEmpty() && notifyIfSuccessful) {
692             buf.append(padding).append("----- NO ERRORS TO REPORT -----\nThis file will be processed by the accounting cycle.\n");
693         }
694         else if (!errorMessages.isEmpty()) {
695             for (String currentMessage : errorMessages) {
696                 buf.append(padding).append(currentMessage + "\n");
697             }
698             buf.append("\n").append(padding).append("----- THIS FILE WAS NOT PROCESSED AND WILL NEED TO BE CORRECTED AND RESUBMITTED -----\n");
699         }
700     }
701 
702     
703 
704 
705 
706 
707 
708 
709     protected String createDemergerMessageBody(CollectorBatch batch, CollectorReportData collectorReportData) {
710         StringBuilder buf = new StringBuilder();
711         appendHeaderInformation(buf, batch, collectorReportData);
712 
713         Map<Transaction, List<Message>> batchOriginEntryScrubberErrors = collectorReportData.getBatchOriginEntryScrubberErrors(batch);
714 
715         
716         
717         Map<DocumentGroupData, OriginEntryTotals> errorGroupDocumentTotals = collectorReportData.getTotalsOnInputOriginEntriesAssociatedWithErrorGroup(batch);
718         Set<DocumentGroupData> errorDocumentGroups = null;
719         if (errorGroupDocumentTotals == null) {
720             return null;
721         }
722         errorDocumentGroups = errorGroupDocumentTotals.keySet();
723         if (errorDocumentGroups.isEmpty()) {
724             return null;
725         }
726         else {
727             for (DocumentGroupData errorDocumentGroup : errorDocumentGroups) {
728                 buf.append("Document ").append(errorDocumentGroup.getDocumentNumber()).append(" Rejected Due to Editing Errors.\n");
729                 for (Transaction transaction : batchOriginEntryScrubberErrors.keySet()) {
730                     if (errorDocumentGroup.matchesTransaction(transaction)) {
731                         if (transaction instanceof OriginEntryFull) {
732                             OriginEntryFull entry = (OriginEntryFull) transaction;
733                             buf.append("     Origin Entry: ").append(entry.getLine()).append("\n");
734                             for (Message message : batchOriginEntryScrubberErrors.get(transaction)) {
735                                 buf.append("          ").append(message.getMessage()).append("\n");
736                             }
737                         }
738                     }
739                 }
740             }
741         }
742 
743         return buf.toString();
744     }
745 
746     public void setMailService(MailService mailService) {
747         this.mailService = mailService;
748     }
749 
750     public void setConfigurationService(ConfigurationService configurationService) {
751         this.configurationService = configurationService;
752     }
753 
754     public void setParameterService(ParameterService parameterService) {
755         this.parameterService = parameterService;
756     }
757 
758     
759 
760 
761 
762     public void setCollectorReportWriterService(ReportWriterService collectorReportWriterService) {
763         this.collectorReportWriterService = collectorReportWriterService;
764     }
765 
766     public void setPreScrubberService(PreScrubberService preScrubberService) {
767         this.preScrubberService = preScrubberService;
768     }
769     
770     protected static class KualiDecimalFormatter implements Formattable {
771         private KualiDecimal number;
772         
773         public KualiDecimalFormatter(KualiDecimal numberToFormat) {
774             this.number = numberToFormat;
775         }
776         
777         @Override
778         public void formatTo(Formatter formatter, int flags, int width, int precision) {
779             Map<String, String> settings = new HashMap<String, String>();
780             settings.put(CurrencyFormatter.SHOW_SYMBOL, Boolean.TRUE.toString());
781             org.kuali.rice.core.web.format.Formatter cf = org.kuali.rice.core.web.format.Formatter.getFormatter(KualiDecimal.class, settings);
782             formatter.format((String) cf.format(number));
783         }
784     }
785 }