View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    *
4    * Copyright 2005-2014 The Kuali Foundation
5    *
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   *
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.module.ar.service.impl;
20  
21  import java.io.IOException;
22  import java.sql.Date;
23  import java.text.MessageFormat;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Properties;
29  
30  import javax.mail.MessagingException;
31  import javax.mail.Session;
32  
33  import org.apache.commons.io.IOUtils;
34  import org.apache.commons.lang.StringUtils;
35  import org.kuali.kfs.coa.businessobject.Organization;
36  import org.kuali.kfs.integration.cg.ContractsAndGrantsBillingAward;
37  import org.kuali.kfs.module.ar.ArConstants;
38  import org.kuali.kfs.module.ar.ArKeyConstants;
39  import org.kuali.kfs.module.ar.ArPropertyConstants;
40  import org.kuali.kfs.module.ar.batch.UpcomingMilestoneNotificationStep;
41  import org.kuali.kfs.module.ar.businessobject.CustomerAddress;
42  import org.kuali.kfs.module.ar.businessobject.InvoiceAddressDetail;
43  import org.kuali.kfs.module.ar.businessobject.Milestone;
44  import org.kuali.kfs.module.ar.document.ContractsGrantsInvoiceDocument;
45  import org.kuali.kfs.module.ar.service.AREmailService;
46  import org.kuali.kfs.module.ar.service.ContractsGrantsBillingUtilityService;
47  import org.kuali.kfs.sys.KFSConstants;
48  import org.kuali.kfs.sys.KFSPropertyConstants;
49  import org.kuali.kfs.sys.mail.AttachmentMailMessage;
50  import org.kuali.kfs.sys.service.AttachmentMailService;
51  import org.kuali.kfs.sys.service.NonTransactional;
52  import org.kuali.rice.core.api.config.property.ConfigContext;
53  import org.kuali.rice.core.api.config.property.ConfigurationService;
54  import org.kuali.rice.core.api.mail.MailMessage;
55  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
56  import org.kuali.rice.kns.service.DataDictionaryService;
57  import org.kuali.rice.krad.bo.Attachment;
58  import org.kuali.rice.krad.bo.Note;
59  import org.kuali.rice.krad.exception.InvalidAddressException;
60  import org.kuali.rice.krad.service.BusinessObjectService;
61  import org.kuali.rice.krad.service.DocumentService;
62  import org.kuali.rice.krad.service.KualiModuleService;
63  import org.kuali.rice.krad.service.NoteService;
64  import org.kuali.rice.krad.service.impl.MailServiceImpl;
65  import org.kuali.rice.krad.util.ObjectUtils;
66  
67  
68  /**
69   * Defines methods for sending AR emails.
70   */
71  public class AREmailServiceImpl implements AREmailService {
72      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AREmailServiceImpl.class);
73  
74      protected AttachmentMailService mailService;
75      protected ParameterService parameterService;
76      protected DataDictionaryService dataDictionaryService;
77      protected ConfigurationService kualiConfigurationService;
78      protected BusinessObjectService businessObjectService;
79      protected DocumentService documentService;
80      protected NoteService noteService;
81      protected KualiModuleService kualiModuleService;
82      protected ContractsGrantsBillingUtilityService contractsGrantsBillingUtilityService;
83  
84      /**
85       * Sets the kualiModuleService attribute value.
86       *
87       * @param kualiModuleService The kualiModuleService to set.
88       */
89      @NonTransactional
90      public void setKualiModuleService(KualiModuleService kualiModuleService) {
91          this.kualiModuleService = kualiModuleService;
92      }
93  
94      public void setBusinessObjectService(BusinessObjectService businessObjectService) {
95          this.businessObjectService = businessObjectService;
96      }
97  
98      /**
99       * This method gets the document service
100      *
101      * @return the document service
102      */
103     public DocumentService getDocumentService() {
104         return documentService;
105     }
106 
107     /**
108      * This method sets the document service
109      *
110      * @param documentService
111      */
112     public void setDocumentService(DocumentService documentService) {
113         this.documentService = documentService;
114     }
115     /**
116      * This method is used to send emails to the agency
117      *
118      * @param invoices
119      */
120     @Override
121     public boolean sendInvoicesViaEmail(Collection<ContractsGrantsInvoiceDocument> invoices) throws InvalidAddressException, MessagingException  {
122         LOG.debug("sendInvoicesViaEmail() starting.");
123 
124         boolean success = true;
125         Properties props = getConfigProperties();
126 
127         // Get session
128         Session session = Session.getInstance(props, null);
129         for (ContractsGrantsInvoiceDocument invoice : invoices) {
130             List<InvoiceAddressDetail> invoiceAddressDetails = invoice.getInvoiceAddressDetails();
131             for (InvoiceAddressDetail invoiceAddressDetail : invoiceAddressDetails) {
132                 if (ArConstants.InvoiceTransmissionMethod.EMAIL.equals(invoiceAddressDetail.getInvoiceTransmissionMethodCode())) {
133                     Note note = noteService.getNoteByNoteId(invoiceAddressDetail.getNoteId());
134 
135                     if (ObjectUtils.isNotNull(note)) {
136                         AttachmentMailMessage message = new AttachmentMailMessage();
137 
138                         String sender = parameterService.getParameterValueAsString(KFSConstants.OptionalModuleNamespaces.ACCOUNTS_RECEIVABLE, ArConstants.CONTRACTS_GRANTS_INVOICE_COMPONENT, ArConstants.FROM_EMAIL_ADDRESS);
139                         message.setFromAddress(sender);
140 
141                         CustomerAddress customerAddress = invoiceAddressDetail.getCustomerAddress();
142                         String recipients = invoiceAddressDetail.getCustomerEmailAddress();
143                         if (StringUtils.isNotEmpty(recipients)) {
144                             message.getToAddresses().add(recipients);
145                         }
146                         else {
147                             LOG.warn("No recipients indicated.");
148                         }
149 
150                         String subject = getSubject(invoice);
151                         message.setSubject(subject);
152                         if (StringUtils.isEmpty(subject)) {
153                             LOG.warn("Empty subject being sent.");
154                         }
155 
156                         String bodyText = getMessageBody(invoice, customerAddress);
157                         message.setMessage(bodyText);
158                         if (StringUtils.isEmpty(bodyText)) {
159                             LOG.warn("Empty bodyText being sent.");
160                         }
161 
162                         Attachment attachment = note.getAttachment();
163                         if (ObjectUtils.isNotNull(attachment)) {
164                             try {
165                                 message.setContent(IOUtils.toByteArray(attachment.getAttachmentContents()));
166                             }
167                             catch (IOException ex) {
168                                 LOG.error("Error setting attachment contents", ex);
169                                 throw new RuntimeException(ex);
170                             }
171                             message.setFileName(attachment.getAttachmentFileName());
172                             message.setType(attachment.getAttachmentMimeTypeCode());
173                         }
174 
175                         setupMailServiceForNonProductionInstance();
176                         mailService.sendMessage(message);
177 
178                         invoiceAddressDetail.setInitialTransmissionDate(new Date(new java.util.Date().getTime()));
179                         documentService.updateDocument(invoice);
180                     } else {
181                         success = false;
182                     }
183                 }
184             }
185         }
186         return success;
187     }
188 
189     protected String getSubject(ContractsGrantsInvoiceDocument invoice) {
190         String subject = kualiConfigurationService.getPropertyValueAsString(ArKeyConstants.CGINVOICE_EMAIL_SUBJECT);
191 
192         return MessageFormat.format(subject, invoice.getInvoiceGeneralDetail().getAward().getProposal().getGrantNumber(),
193                 invoice.getInvoiceGeneralDetail().getProposalNumber(),
194                 invoice.getDocumentNumber());
195     }
196 
197     protected String getMessageBody(ContractsGrantsInvoiceDocument invoice, CustomerAddress customerAddress) {
198         String message = kualiConfigurationService.getPropertyValueAsString(ArKeyConstants.CGINVOICE_EMAIL_BODY);
199 
200         String department = "";
201         String[] orgCode = invoice.getInvoiceGeneralDetail().getAward().getAwardPrimaryFundManager().getFundManager().getPrimaryDepartmentCode().split("-");
202         Map<String, Object> key = new HashMap<String, Object>();
203         key.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, orgCode[0].trim());
204         key.put(KFSPropertyConstants.ORGANIZATION_CODE, orgCode[1].trim());
205         Organization org = businessObjectService.findByPrimaryKey(Organization.class, key);
206         if (ObjectUtils.isNotNull(org)) {
207             department = org.getOrganizationName();
208         }
209 
210         return MessageFormat.format(message, customerAddress.getCustomer().getCustomerName(),
211                 customerAddress.getCustomerAddressName(),
212                 invoice.getInvoiceGeneralDetail().getAward().getAwardPrimaryFundManager().getFundManager().getName(),
213                 invoice.getInvoiceGeneralDetail().getAward().getAwardPrimaryFundManager().getProjectTitle(),
214                 department,
215                 invoice.getInvoiceGeneralDetail().getAward().getAwardPrimaryFundManager().getFundManager().getPhoneNumber(),
216                 invoice.getInvoiceGeneralDetail().getAward().getAwardPrimaryFundManager().getFundManager().getEmailAddress());
217     }
218 
219     /**
220      * Setup properties to handle mail messages in a non-production environment as appropriate.
221      *
222      * NOTE: We should be setting up configuration properties for these values, and once that is done
223      * this method can be removed.
224      */
225     public void setupMailServiceForNonProductionInstance() {
226         if (!ConfigContext.getCurrentContextConfig().isProductionEnvironment()) {
227             ((MailServiceImpl)mailService).setRealNotificationsEnabled(false);
228             ((MailServiceImpl)mailService).setNonProductionNotificationMailingList(mailService.getBatchMailingList());
229         }
230     }
231 
232     /**
233      * Sets the mailService attribute value.
234      *
235      * @param mailService The mailService to set.
236      */
237     public void setMailService(AttachmentMailService mailService) {
238         this.mailService = mailService;
239     }
240 
241     /**
242      * Sets the parameterService attribute value.
243      *
244      * @param parameterService The parameterService to set.
245      */
246     public void setParameterService(ParameterService parameterService) {
247         this.parameterService = parameterService;
248     }
249 
250     /**
251      * Sets the dataDictionaryService attribute value.
252      *
253      * @param dataDictionaryService The dataDictionaryService to set.
254      */
255     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
256         this.dataDictionaryService = dataDictionaryService;
257     }
258 
259     /**
260      * Retrieves the Properties used to configure the Mailer. The property names configured in the Workflow configuration should
261      * match those of Java Mail.
262      *
263      * @return
264      */
265     protected Properties getConfigProperties() {
266         return ConfigContext.getCurrentContextConfig().getProperties();
267     }
268 
269     /**
270      * This method sends out emails for upcoming milestones.
271      *
272      * @see org.kuali.kfs.module.ar.service.CGEmailService#sendEmail(java.util.List, org.kuali.kfs.module.ar.businessobject.Award)
273      */
274     @Override
275     public void sendEmailNotificationsForMilestones(List<Milestone> milestones, ContractsAndGrantsBillingAward award) {
276         LOG.debug("sendEmail() starting");
277 
278         MailMessage message = new MailMessage();
279 
280         message.setFromAddress(mailService.getBatchMailingList());
281         message.setSubject(getEmailSubject(ArConstants.REMINDER_EMAIL_SUBJECT));
282         message.getToAddresses().add(award.getAwardPrimaryFundManager().getFundManager().getEmailAddress());
283         StringBuffer body = new StringBuffer();
284 
285         String messageKey = kualiConfigurationService.getPropertyValueAsString(ArKeyConstants.MESSAGE_CG_UPCOMING_MILESTONES_EMAIL_LINE_1);
286         body.append(messageKey + "\n\n");
287 
288         for (Milestone milestone : milestones) {
289             String proposalNumber = dataDictionaryService.getAttributeLabel(Milestone.class, KFSPropertyConstants.PROPOSAL_NUMBER);
290             String milestoneNumber = dataDictionaryService.getAttributeLabel(Milestone.class, ArPropertyConstants.MilestoneFields.MILESTONE_NUMBER);
291             String milestoneDescription = dataDictionaryService.getAttributeLabel(Milestone.class, ArPropertyConstants.MilestoneFields.MILESTONE_DESCRIPTION);
292             String milestoneAmount = dataDictionaryService.getAttributeLabel(Milestone.class, ArPropertyConstants.MilestoneFields.MILESTONE_AMOUNT);
293             String milestoneExpectedCompletionDate = dataDictionaryService.getAttributeLabel(Milestone.class, ArPropertyConstants.MilestoneFields.MILESTONE_EXPECTED_COMPLETION_DATE);
294 
295             body.append(proposalNumber + ": " + milestone.getProposalNumber() + " \n");
296             body.append(milestoneNumber + ": " + milestone.getMilestoneNumber() + " \n");
297             body.append(milestoneDescription + ": " + milestone.getMilestoneDescription() + " \n");
298             body.append(milestoneAmount + ": " + milestone.getMilestoneAmount() + " \n");
299             body.append(milestoneExpectedCompletionDate + ": " + milestone.getMilestoneExpectedCompletionDate() + " \n");
300 
301             body.append("\n\n");
302         }
303         body.append("\n\n");
304 
305         messageKey = kualiConfigurationService.getPropertyValueAsString(ArKeyConstants.MESSAGE_CG_UPCOMING_MILESTONES_EMAIL_LINE_2);
306         body.append(MessageFormat.format(messageKey, new Object[] { null }) + "\n\n");
307 
308         message.setMessage(body.toString());
309 
310         try {
311             mailService.sendMessage(message);
312         }
313         catch (InvalidAddressException | MessagingException ex) {
314             LOG.error("Problems sending milestones e-mail", ex);
315             throw new RuntimeException("Problems sending milestones e-mail", ex);
316         }
317     }
318 
319     /**
320      * Retrieves the email subject text from system parameter then checks environment code and prepends to message if not
321      * production.
322      *
323      * @param subjectParmaterName name of parameter giving the subject text
324      * @return subject text
325      */
326     protected String getEmailSubject(String subjectParmaterName) {
327         String subject = parameterService.getParameterValueAsString(UpcomingMilestoneNotificationStep.class, subjectParmaterName);
328 
329         String productionEnvironmentCode = kualiConfigurationService.getPropertyValueAsString(KFSConstants.PROD_ENVIRONMENT_CODE_KEY);
330         String environmentCode = kualiConfigurationService.getPropertyValueAsString(KFSConstants.ENVIRONMENT_KEY);
331         if (!StringUtils.equals(productionEnvironmentCode, environmentCode)) {
332             subject = environmentCode + ": " + subject;
333         }
334 
335         return subject;
336     }
337 
338     /**
339      * Sets the noteService attribute value.
340      *
341      * @param noteService The noteService to set.
342      */
343     public void setNoteService(NoteService noteService) {
344         this.noteService = noteService;
345     }
346 
347     public ConfigurationService getKualiConfigurationService() {
348         return kualiConfigurationService;
349     }
350 
351     public void setKualiConfigurationService(ConfigurationService kualiConfigurationService) {
352         this.kualiConfigurationService = kualiConfigurationService;
353     }
354 
355     public ContractsGrantsBillingUtilityService getContractsGrantsBillingUtilityService() {
356         return contractsGrantsBillingUtilityService;
357     }
358 
359     public void setContractsGrantsBillingUtilityService(ContractsGrantsBillingUtilityService contractsGrantsBillingUtilityService) {
360         this.contractsGrantsBillingUtilityService = contractsGrantsBillingUtilityService;
361     }
362 }