This is an automated email from the ASF dual-hosted git repository.

ptuomola pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 8146a73  FINERACT-1273: Triggered emails
8146a73 is described below

commit 8146a7399c2ed936f7d353f0f4777bb4e5006d76
Author: Adonay <[email protected]>
AuthorDate: Thu May 20 16:41:32 2021 -0500

    FINERACT-1273: Triggered emails
    
    Basically send an email when a client does an action: for now when a loan 
is approved, rejected and when a repayment is done. More triggers should be 
added in the future
---
 .../campaigns/email/domain/EmailCampaign.java      |  12 +-
 .../email/domain/EmailCampaignRepository.java      |   9 +-
 .../campaigns/email/domain/EmailCampaignType.java  |  10 +-
 .../EmailCampaignDomainService.java}               |   7 +-
 .../service/EmailCampaignDomainServiceImpl.java    | 151 +++++++++++++++++++++
 ...ailCampaignWritePlatformCommandHandlerImpl.java |  80 +++++++----
 .../service/EmailCampaignWritePlatformService.java |   5 +
 .../service/EmailMessageJobEmailServiceImpl.java   |  64 ++++-----
 .../AddressCommandFromApiJsonDeserializer.java     |   7 +-
 .../core_db/V367__reports_for_email_campaigns.sql  |  26 ++++
 ...sql => V370__message_gateway_hook_template.sql} |   0
 .../integrationtests/client/ReportsTest.java       |   2 +-
 12 files changed, 287 insertions(+), 86 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java
index 272f219..72ac226 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java
@@ -75,11 +75,11 @@ public class EmailCampaign extends 
AbstractPersistableCustom {
     @Column(name = "email_message", nullable = false)
     private String emailMessage;
 
-    @Column(name = "email_attachment_file_format", nullable = false)
+    @Column(name = "email_attachment_file_format")
     private String emailAttachmentFileFormat;
 
     @ManyToOne
-    @JoinColumn(name = "stretchy_report_id", nullable = false)
+    @JoinColumn(name = "stretchy_report_id")
     private Report stretchyReport;
 
     @Column(name = "stretchy_report_param_map", nullable = true)
@@ -109,18 +109,18 @@ public class EmailCampaign extends 
AbstractPersistableCustom {
     @JoinColumn(name = "approvedon_userid", nullable = true)
     private AppUser approvedBy;
 
-    @Column(name = "recurrence", nullable = false)
+    @Column(name = "recurrence")
     private String recurrence;
 
-    @Column(name = "next_trigger_date", nullable = false)
+    @Column(name = "next_trigger_date")
     @Temporal(TemporalType.TIMESTAMP)
     private Date nextTriggerDate;
 
-    @Column(name = "last_trigger_date", nullable = false)
+    @Column(name = "last_trigger_date")
     @Temporal(TemporalType.TIMESTAMP)
     private Date lastTriggerDate;
 
-    @Column(name = "recurrence_start_date", nullable = false)
+    @Column(name = "recurrence_start_date")
     @Temporal(TemporalType.TIMESTAMP)
     private Date recurrenceStartDate;
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
index 291698b..8a0eec2 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
@@ -18,7 +18,14 @@
  */
 package org.apache.fineract.infrastructure.campaigns.email.domain;
 
+import java.util.List;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
 
-public interface EmailCampaignRepository extends JpaRepository<EmailCampaign, 
Long>, JpaSpecificationExecutor<EmailCampaign> {}
+public interface EmailCampaignRepository extends JpaRepository<EmailCampaign, 
Long>, JpaSpecificationExecutor<EmailCampaign> {
+
+    @Query("SELECT campaign FROM EmailCampaign campaign WHERE 
campaign.paramValue LIKE :reportPattern AND campaign.campaignType=:type AND 
campaign.status=300")
+    List<EmailCampaign> findActiveEmailCampaigns(@Param("reportPattern") 
String reportPattern, @Param("type") Integer type);
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java
index 95c36ce..79059ea 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java
@@ -20,7 +20,8 @@ package 
org.apache.fineract.infrastructure.campaigns.email.domain;
 
 public enum EmailCampaignType {
 
-    DIRECT(1, "emailCampaignStatusType.direct"), SCHEDULE(2, 
"emailCampaignStatusType.schedule");
+    DIRECT(1, "emailCampaignStatusType.direct"), SCHEDULE(2, 
"emailCampaignStatusType.schedule"), TRIGGERED(3,
+            "emailCampaignStatusType.triggered");
 
     private final Integer value;
     private final String code;
@@ -47,6 +48,9 @@ public enum EmailCampaignType {
             case 2:
                 type = SCHEDULE;
             break;
+            case 3:
+                type = TRIGGERED;
+            break;
         }
         return type;
     }
@@ -58,4 +62,8 @@ public enum EmailCampaignType {
     public boolean isSchedule() {
         return this.value.equals(EmailCampaignType.SCHEDULE.getValue());
     }
+
+    public boolean isTriggered() {
+        return this.value.equals(EmailCampaignType.TRIGGERED.getValue());
+    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignDomainService.java
similarity index 70%
copy from 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
copy to 
fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignDomainService.java
index 291698b..c02d697 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignDomainService.java
@@ -16,9 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.infrastructure.campaigns.email.domain;
+package org.apache.fineract.infrastructure.campaigns.email.service;
 
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-
-public interface EmailCampaignRepository extends JpaRepository<EmailCampaign, 
Long>, JpaSpecificationExecutor<EmailCampaign> {}
+public interface EmailCampaignDomainService {}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignDomainServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignDomainServiceImpl.java
new file mode 100644
index 0000000..31342a9
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignDomainServiceImpl.java
@@ -0,0 +1,151 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.campaigns.email.service;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.PostConstruct;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaign;
+import 
org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignRepository;
+import 
org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
+import org.apache.fineract.portfolio.common.BusinessEventNotificationConstants;
+import org.apache.fineract.portfolio.common.service.BusinessEventListener;
+import 
org.apache.fineract.portfolio.common.service.BusinessEventNotifierService;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EmailCampaignDomainServiceImpl implements 
EmailCampaignDomainService {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(EmailCampaignDomainServiceImpl.class);
+    private final BusinessEventNotifierService businessEventNotifierService;
+    private final EmailCampaignWritePlatformService 
emailCampaignWritePlatformService;
+    private final EmailCampaignRepository emailCampaignRepository;
+
+    @Autowired
+    public EmailCampaignDomainServiceImpl(BusinessEventNotifierService 
businessEventNotifierService,
+            EmailCampaignWritePlatformService 
emailCampaignWritePlatformService, EmailCampaignRepository 
emailCampaignRepository) {
+        this.businessEventNotifierService = businessEventNotifierService;
+        this.emailCampaignWritePlatformService = 
emailCampaignWritePlatformService;
+        this.emailCampaignRepository = emailCampaignRepository;
+    }
+
+    @PostConstruct
+    public void addListeners() {
+        
this.businessEventNotifierService.addBusinessEventPostListeners(BusinessEventNotificationConstants.BusinessEvents.LOAN_APPROVED,
+                new EmailCampaignDomainServiceImpl.SendEmailOnLoanApproved());
+        
this.businessEventNotifierService.addBusinessEventPostListeners(BusinessEventNotificationConstants.BusinessEvents.LOAN_REJECTED,
+                new EmailCampaignDomainServiceImpl.SendEmailOnLoanRejected());
+        this.businessEventNotifierService.addBusinessEventPostListeners(
+                
BusinessEventNotificationConstants.BusinessEvents.LOAN_MAKE_REPAYMENT,
+                new EmailCampaignDomainServiceImpl.SendEmailOnLoanRepayment());
+    }
+
+    private class SendEmailOnLoanRepayment extends EmailBusinessEventAdapter {
+
+        @Override
+        public void 
businessEventWasExecuted(Map<BusinessEventNotificationConstants.BusinessEntity, 
Object> businessEventEntity) {
+            Object entity = 
businessEventEntity.get(BusinessEventNotificationConstants.BusinessEntity.LOAN_TRANSACTION);
+            if (entity instanceof LoanTransaction) {
+                LoanTransaction loanTransaction = (LoanTransaction) entity;
+                try {
+                    notifyLoanOwner(loanTransaction, "Loan Repayment");
+                } catch (IOException e) {
+                    LOG.error("Exception when trying to send triggered email: 
{}", e.getMessage());
+                }
+            }
+        }
+    }
+
+    private class SendEmailOnLoanRejected extends EmailBusinessEventAdapter {
+
+        @Override
+        public void 
businessEventWasExecuted(Map<BusinessEventNotificationConstants.BusinessEntity, 
Object> businessEventEntity) {
+            Object entity = 
businessEventEntity.get(BusinessEventNotificationConstants.BusinessEntity.LOAN);
+            if (entity instanceof Loan) {
+                Loan loan = (Loan) entity;
+                try {
+                    notifyLoanOwner(loan, "Loan Rejected");
+                } catch (IOException e) {
+                    LOG.error("Exception when trying to send triggered email: 
{}", e.getMessage());
+                }
+            }
+        }
+    }
+
+    private class SendEmailOnLoanApproved extends EmailBusinessEventAdapter {
+
+        @Override
+        public void 
businessEventWasExecuted(Map<BusinessEventNotificationConstants.BusinessEntity, 
Object> businessEventEntity) {
+            Object entity = 
businessEventEntity.get(BusinessEventNotificationConstants.BusinessEntity.LOAN);
+            if (entity instanceof Loan) {
+                Loan loan = (Loan) entity;
+                try {
+                    notifyLoanOwner(loan, "Loan Approved");
+                } catch (IOException e) {
+                    LOG.error("Exception when trying to send triggered email: 
{}", e.getMessage());
+                }
+            }
+        }
+    }
+
+    private void notifyLoanOwner(LoanTransaction loanTransaction, String 
paramValue) throws IOException {
+        List<EmailCampaign> campaigns = 
this.retrieveEmailCampaigns(paramValue);
+        for (EmailCampaign emailCampaign : campaigns) {
+            HashMap<String, String> campaignParams = new 
ObjectMapper().readValue(emailCampaign.getParamValue(),
+                    new TypeReference<HashMap<String, String>>() {});
+            campaignParams.put("loanId", 
loanTransaction.getLoan().getId().toString());
+            campaignParams.put("loanTransactionId", 
loanTransaction.getId().toString());
+            
this.emailCampaignWritePlatformService.insertDirectCampaignIntoEmailOutboundTable(loanTransaction.getLoan(),
 emailCampaign,
+                    campaignParams);
+        }
+    }
+
+    private void notifyLoanOwner(Loan loan, String paramValue) throws 
IOException {
+        List<EmailCampaign> campaigns = 
this.retrieveEmailCampaigns(paramValue);
+        for (EmailCampaign emailCampaign : campaigns) {
+            HashMap<String, String> campaignParams = new 
ObjectMapper().readValue(emailCampaign.getParamValue(),
+                    new TypeReference<HashMap<String, String>>() {});
+            campaignParams.put("loanId", loan.getId().toString());
+            
this.emailCampaignWritePlatformService.insertDirectCampaignIntoEmailOutboundTable(loan,
 emailCampaign, campaignParams);
+        }
+    }
+
+    private abstract static class EmailBusinessEventAdapter implements 
BusinessEventListener {
+
+        @Override
+        public void 
businessEventToBeExecuted(Map<BusinessEventNotificationConstants.BusinessEntity,
 Object> businessEventEntity) {
+            // Nothing to do
+        }
+    }
+
+    private List<EmailCampaign> retrieveEmailCampaigns(String paramValue) {
+        List<EmailCampaign> emailCampaigns = 
emailCampaignRepository.findActiveEmailCampaigns("%" + paramValue + "%",
+                SmsCampaignTriggerType.TRIGGERED.getValue());
+        return emailCampaigns;
+    }
+}
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java
index 5ba082d..543c23b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java
@@ -158,20 +158,24 @@ public class EmailCampaignWritePlatformCommandHandlerImpl 
implements EmailCampai
 
         final Long reportId = 
command.longValueOfParameterNamed(EmailCampaignValidator.stretchyReportId);
 
-        final Report report = 
this.reportRepository.findById(reportId).orElseThrow(() -> new 
ReportNotFoundException(reportId));
-
-        // find all report parameters and store them as json string
-        final Set<ReportParameterUsage> reportParameterUsages = 
report.getReportParameterUsages();
-        final Map<String, String> stretchyReportParams = new HashMap<>();
-
-        if (reportParameterUsages != null && !reportParameterUsages.isEmpty()) 
{
-            for (final ReportParameterUsage reportParameterUsage : 
reportParameterUsages) {
-                
stretchyReportParams.put(reportParameterUsage.getReportParameterName(), "");
+        Report report = null;
+        Map<String, String> stretchyReportParams = null;
+        if (reportId != null) {
+            report = this.reportRepository.findById(reportId).orElseThrow(() 
-> new ReportNotFoundException(reportId));
+            final Set<ReportParameterUsage> reportParameterUsages = 
report.getReportParameterUsages();
+            stretchyReportParams = new HashMap<>();
+
+            if (reportParameterUsages != null && 
!reportParameterUsages.isEmpty()) {
+                for (final ReportParameterUsage reportParameterUsage : 
reportParameterUsages) {
+                    
stretchyReportParams.put(reportParameterUsage.getReportParameterName(), "");
+                }
             }
         }
 
         EmailCampaign emailCampaign = EmailCampaign.instance(currentUser, 
businessRule, report, command);
-        emailCampaign.setStretchyReportParamMap(new 
Gson().toJson(stretchyReportParams));
+        if (stretchyReportParams != null) {
+            emailCampaign.setStretchyReportParamMap(new 
Gson().toJson(stretchyReportParams));
+        }
 
         this.emailCampaignRepository.save(emailCampaign);
 
@@ -241,6 +245,32 @@ public class EmailCampaignWritePlatformCommandHandlerImpl 
implements EmailCampai
 
     }
 
+    @Override
+    public void insertDirectCampaignIntoEmailOutboundTable(final Loan loan, 
final EmailCampaign emailCampaign,
+            HashMap<String, String> campaignParams) {
+        try {
+            List<HashMap<String, Object>> runReportObject = 
this.getRunReportByServiceImpl(campaignParams.get("reportName"),
+                    campaignParams);
+
+            if (runReportObject != null) {
+                for (HashMap<String, Object> entry : runReportObject) {
+                    String message = 
this.compileEmailTemplate(emailCampaign.getEmailMessage(), 
emailCampaign.getCampaignName(), entry);
+                    Client client = loan.getClient();
+                    String emailAddress = client.emailAddress();
+
+                    if (emailAddress != null && isValidEmail(emailAddress)) {
+                        EmailMessage emailMessage = 
EmailMessage.pendingEmail(null, client, null, emailCampaign,
+                                emailCampaign.getEmailSubject(), message, 
emailAddress, emailCampaign.getCampaignName());
+                        this.emailMessageRepository.save(emailMessage);
+                    }
+                }
+            }
+        } catch (final IOException e) {
+            // TODO throw something here
+        }
+
+    }
+
     private void insertDirectCampaignIntoEmailOutboundTable(final String 
emailParams, final String emailSubject,
             final String messageTemplate, final String campaignName, final 
Long campaignId) {
         try {
@@ -589,8 +619,11 @@ public class EmailCampaignWritePlatformCommandHandlerImpl 
implements EmailCampai
                     final EmailCampaign emailCampaign = 
this.emailCampaignRepository.findById(emailMessage.getEmailCampaign().getId())
                             .orElse(null); //
 
-                    final ScheduledEmailAttachmentFileFormat 
emailAttachmentFileFormat = ScheduledEmailAttachmentFileFormat
-                            
.instance(emailCampaign.getEmailAttachmentFileFormat());
+                    ScheduledEmailAttachmentFileFormat 
emailAttachmentFileFormat = null;
+                    if (emailCampaign.getEmailAttachmentFileFormat() != null) {
+                        emailAttachmentFileFormat = 
ScheduledEmailAttachmentFileFormat
+                                
.instance(emailCampaign.getEmailAttachmentFileFormat());
+                    }
 
                     final List<File> attachmentList = new ArrayList<>();
 
@@ -690,29 +723,16 @@ public class EmailCampaignWritePlatformCommandHandlerImpl 
implements EmailCampai
 
                     final EmailMessageWithAttachmentData 
emailMessageWithAttachmentData = EmailMessageWithAttachmentData.createNew(
                             emailMessage.getEmailAddress(), 
emailMessage.getMessage(), emailMessage.getEmailSubject(), attachmentList);
-
-                    if (!attachmentList.isEmpty() && attachmentList.size() > 
0) { // only
-                                                                               
   // send
-                                                                               
   // email
-                                                                               
   // message
-                                                                               
   // if
-                                                                               
   // there
-                                                                               
   // is
-                                                                               
   // an
-                                                                               
   // attachment
-                                                                               
   // to
-                                                                               
   // it
+                    try {
 
                         
this.emailMessageJobEmailService.sendEmailWithAttachment(emailMessageWithAttachmentData);
 
                         
emailMessage.setStatusType(EmailMessageStatusType.SENT.getValue());
 
                         this.emailMessageRepository.save(emailMessage);
-                    } else {
-                        emailMessage.updateErrorMessage(errorLog.toString());
-
+                    } catch (Exception e) {
+                        emailMessage.updateErrorMessage(e.getMessage());
                         
emailMessage.setStatusType(EmailMessageStatusType.FAILED.getValue());
-
                         this.emailMessageRepository.save(emailMessage);
                     }
                 }
@@ -734,7 +754,9 @@ public class EmailCampaignWritePlatformCommandHandlerImpl 
implements EmailCampai
      */
     private File generateAttachments(final EmailCampaign emailCampaign, final 
ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat,
             final Map<String, String> reportParams, final String reportName, 
final StringBuilder errorLog) {
-
+        if (reportName == null) {
+            return null;
+        }
         try {
             final ByteArrayOutputStream byteArrayOutputStream = 
this.readReportingService.generatePentahoReportAsOutputStream(reportName,
                     emailAttachmentFileFormat.getValue(), reportParams, null, 
emailCampaign.getApprovedBy(), errorLog);
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java
index 9819131..46601de 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java
@@ -18,11 +18,14 @@
  */
 package org.apache.fineract.infrastructure.campaigns.email.service;
 
+import java.util.HashMap;
 import 
org.apache.fineract.infrastructure.campaigns.email.data.PreviewCampaignMessage;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaign;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.api.JsonQuery;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 
 public interface EmailCampaignWritePlatformService {
 
@@ -44,4 +47,6 @@ public interface EmailCampaignWritePlatformService {
 
     void sendEmailMessage() throws JobExecutionException;
 
+    void insertDirectCampaignIntoEmailOutboundTable(Loan loan, EmailCampaign 
emailCampaign, HashMap<String, String> campaignParams);
+
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java
index 5194644..c9abac4 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java
@@ -23,10 +23,9 @@ import java.util.List;
 import java.util.Properties;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
-import org.apache.fineract.infrastructure.campaigns.email.EmailApiConstants;
 import 
org.apache.fineract.infrastructure.campaigns.email.data.EmailMessageWithAttachmentData;
-import 
org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfiguration;
-import 
org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository;
+import 
org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData;
+import 
org.apache.fineract.infrastructure.configuration.service.ExternalServicesPropertiesReadPlatformService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -38,30 +37,33 @@ import org.springframework.stereotype.Service;
 public final class EmailMessageJobEmailServiceImpl implements 
EmailMessageJobEmailService {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(EmailMessageJobEmailServiceImpl.class);
-    private EmailConfigurationRepository emailConfigurationRepository;
+    private final ExternalServicesPropertiesReadPlatformService 
externalServicesReadPlatformService;
 
     @Autowired
-    private EmailMessageJobEmailServiceImpl(final EmailConfigurationRepository 
emailConfigurationRepository) {
-        this.emailConfigurationRepository = emailConfigurationRepository;
+    private 
EmailMessageJobEmailServiceImpl(ExternalServicesPropertiesReadPlatformService 
externalServicesReadPlatformService) {
+        this.externalServicesReadPlatformService = 
externalServicesReadPlatformService;
     }
 
     @Override
     public void sendEmailWithAttachment(EmailMessageWithAttachmentData 
emailMessageWithAttachmentData) {
+        final SMTPCredentialsData smtpCredentialsData = 
this.externalServicesReadPlatformService.getSMTPCredentials();
         try {
             JavaMailSenderImpl javaMailSenderImpl = new JavaMailSenderImpl();
-            javaMailSenderImpl.setHost(this.getGmailSmtpServer());
-            javaMailSenderImpl.setPort(this.getGmailSmtpPort());
-            javaMailSenderImpl.setUsername(this.getGmailSmtpUsername());
-            javaMailSenderImpl.setPassword(this.getGmailSmtpPassword());
-            
javaMailSenderImpl.setJavaMailProperties(this.getJavaMailProperties());
+            javaMailSenderImpl.setHost(smtpCredentialsData.getHost());
+            
javaMailSenderImpl.setPort(Integer.parseInt(smtpCredentialsData.getPort()));
+            javaMailSenderImpl.setUsername(smtpCredentialsData.getUsername());
+            javaMailSenderImpl.setPassword(smtpCredentialsData.getPassword());
+            javaMailSenderImpl
+                    
.setJavaMailProperties(this.getJavaMailProperties(smtpCredentialsData, 
javaMailSenderImpl.getJavaMailProperties()));
 
             MimeMessage mimeMessage = javaMailSenderImpl.createMimeMessage();
 
             // use the true flag to indicate you need a multipart message
             MimeMessageHelper mimeMessageHelper = new 
MimeMessageHelper(mimeMessage, true);
 
+            mimeMessageHelper.setFrom(smtpCredentialsData.getFromEmail());
             mimeMessageHelper.setTo(emailMessageWithAttachmentData.getTo());
-            
mimeMessageHelper.setText(emailMessageWithAttachmentData.getText());
+            
mimeMessageHelper.setText(emailMessageWithAttachmentData.getText(), true);
             
mimeMessageHelper.setSubject(emailMessageWithAttachmentData.getSubject());
             final List<File> attachments = 
emailMessageWithAttachmentData.getAttachments();
             if (attachments != null && attachments.size() > 0) {
@@ -80,32 +82,18 @@ public final class EmailMessageJobEmailServiceImpl 
implements EmailMessageJobEma
 
     }
 
-    private String getGmailSmtpServer() {
-        final EmailConfiguration gmailSmtpServer = 
this.emailConfigurationRepository.findByName(EmailApiConstants.SMTP_SERVER);
-        return (gmailSmtpServer != null) ? gmailSmtpServer.getValue() : null;
-    }
-
-    private Integer getGmailSmtpPort() {
-        final EmailConfiguration gmailSmtpPort = 
this.emailConfigurationRepository.findByName(EmailApiConstants.SMTP_PORT);
-        return (gmailSmtpPort != null) ? 
Integer.parseInt(gmailSmtpPort.getValue()) : null;
-    }
-
-    private String getGmailSmtpUsername() {
-        final EmailConfiguration gmailSmtpUsername = 
this.emailConfigurationRepository.findByName(EmailApiConstants.SMTP_USERNAME);
-        return (gmailSmtpUsername != null) ? gmailSmtpUsername.getValue() : 
null;
-    }
-
-    private String getGmailSmtpPassword() {
-        final EmailConfiguration gmailSmtpPassword = 
this.emailConfigurationRepository.findByName(EmailApiConstants.SMTP_PASSWORD);
-        return (gmailSmtpPassword != null) ? gmailSmtpPassword.getValue() : 
null;
-    }
-
-    private Properties getJavaMailProperties() {
-        Properties properties = new Properties();
-        properties.setProperty("mail.smtp.starttls.enable", "true");
-        properties.setProperty("mail.smtp.auth", "true");
-        properties.setProperty("mail.smtp.ssl.trust", 
this.getGmailSmtpServer());
-
+    private Properties getJavaMailProperties(SMTPCredentialsData 
smtpCredentialsData, Properties properties) {
+        properties.put("mail.smtp.starttls.enable", "true");
+        properties.put("mail.transport.protocol", "smtp");
+        properties.put("mail.smtp.auth", "true");
+        properties.put("mail.smtp.ssl.trust", smtpCredentialsData.getHost());
+        if (smtpCredentialsData.isUseTLS()) {
+            // Needs to disable startTLS if the port is 465 in order to send 
the email successfully when using the
+            // smtp.gmail.com as the host
+            if (smtpCredentialsData.getPort().equals("465")) {
+                properties.put("mail.smtp.starttls.enable", "false");
+            }
+        }
         return properties;
     }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/serialization/AddressCommandFromApiJsonDeserializer.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/serialization/AddressCommandFromApiJsonDeserializer.java
index 9b8d37e..39eef97 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/serialization/AddressCommandFromApiJsonDeserializer.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/address/serialization/AddressCommandFromApiJsonDeserializer.java
@@ -74,9 +74,7 @@ public class AddressCommandFromApiJsonDeserializer {
         final List<FieldConfigurationData> configData = 
configurationData.stream().filter(FieldConfigurationData::isEnabled)
                 .collect(Collectors.toList());
 
-        final Set<String> supportedParameters = configData.stream()
-                .map(FieldConfigurationData::getField)
-                .collect(Collectors.toSet());
+        final Set<String> supportedParameters = 
configData.stream().map(FieldConfigurationData::getField).collect(Collectors.toSet());
 
         supportedParameters.add("locale");
         supportedParameters.add("dateFormat");
@@ -85,8 +83,7 @@ public class AddressCommandFromApiJsonDeserializer {
         this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, 
supportedParameters);
 
         configData.forEach(fieldConfiguration -> {
-            final String field =
-                    fieldConfiguration.getField().equals("addressType") ? 
"addressTypeId" : fieldConfiguration.getField();
+            final String field = 
fieldConfiguration.getField().equals("addressType") ? "addressTypeId" : 
fieldConfiguration.getField();
             final String fieldValue = 
this.fromApiJsonHelper.extractStringNamed(field, element);
 
             if (fieldConfiguration.getField().equals("addressType") && 
fromNewClient) {
diff --git 
a/fineract-provider/src/main/resources/sql/migrations/core_db/V367__reports_for_email_campaigns.sql
 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V367__reports_for_email_campaigns.sql
new file mode 100644
index 0000000..86a97fc
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V367__reports_for_email_campaigns.sql
@@ -0,0 +1,26 @@
+--
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements. See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership. The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License. You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied. See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+--
+
+INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, 
`report_sql`, `description`, `core_report`, `use_report`, 
`self_service_user_report`) VALUES ('Loan Approved - Email', 'Email', 
'Triggered', 'select  ml.id as loanId,  ifnull(mc.id,mc2.id) as id,  
ifnull(mc.firstname,mc2.firstname) as firstname,  
\nifnull(mc.middlename,ifnull(mc2.middlename,(\'\'))) as middlename,  
ifnull(mc.lastname,mc2.lastname) as lastname,  
\nifnull(mc.display_name,mc2.display_name) as dis [...]
+INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, 
`report_sql`, `description`, `core_report`, `use_report`, 
`self_service_user_report`) VALUES ('Loan Rejected - Email', 'Email', 
'Triggered', 'select  ml.id as loanId,  ifnull(mc.id,mc2.id) as id,  
ifnull(mc.firstname,mc2.firstname) as firstname,  
\nifnull(mc.middlename,ifnull(mc2.middlename,(\'\'))) as middlename,  
ifnull(mc.lastname,mc2.lastname) as lastname,  
\nifnull(mc.display_name,mc2.display_name) as dis [...]
+INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, 
`report_sql`, `description`, `core_report`, `use_report`, 
`self_service_user_report`) VALUES ('Loan Repayment - Email', 'Email', 
'Triggered', 'select  ml.id as loanId,  ifnull(mc.id,mc2.id) as id,  
ifnull(mc.firstname,mc2.firstname) as firstname,  
\nifnull(mc.middlename,ifnull(mc2.middlename,(\'\'))) as middlename,  
ifnull(mc.lastname,mc2.lastname) as lastname,  
\nifnull(mc.display_name,mc2.display_name) as di [...]
+
+ALTER TABLE `scheduled_email_campaign`
+CHANGE COLUMN `email_attachment_file_format` `email_attachment_file_format` 
VARCHAR(10) NULL ,
+CHANGE COLUMN `stretchy_report_id` `stretchy_report_id` INT(11) NULL ;
diff --git 
a/fineract-provider/src/main/resources/sql/migrations/core_db/V367__message_gateway_hook_template.sql
 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V370__message_gateway_hook_template.sql
similarity index 100%
rename from 
fineract-provider/src/main/resources/sql/migrations/core_db/V367__message_gateway_hook_template.sql
rename to 
fineract-provider/src/main/resources/sql/migrations/core_db/V370__message_gateway_hook_template.sql
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
index f1eda5f..98378b1 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ReportsTest.java
@@ -35,7 +35,7 @@ public class ReportsTest extends IntegrationTest {
 
     @Test
     void listReports() {
-        assertThat(ok(fineract().reports.retrieveReportList())).hasSize(117);
+        assertThat(ok(fineract().reports.retrieveReportList())).hasSize(120);
     }
 
     @Test

Reply via email to