You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by na...@apache.org on 2017/11/20 13:33:54 UTC
[3/7] fineract git commit: Email Campaigns PR
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..66bd97b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java
@@ -0,0 +1,775 @@
+/**
+ * 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 com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.Mustache;
+import com.github.mustachejava.MustacheFactory;
+import com.google.gson.Gson;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+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.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
+import org.apache.fineract.infrastructure.dataqueries.domain.*;
+import org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException;
+import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
+import org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
+import org.apache.fineract.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
+import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailData;
+import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJob;
+import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobEmailAttachmentFileFormat;
+import org.apache.fineract.infrastructure.reportmailingjob.helper.IPv4Helper;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailMessageWithAttachmentData;
+import org.apache.fineract.infrastructure.campaigns.email.domain.*;
+import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignMustBeClosedToBeDeletedException;
+import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignMustBeClosedToEditException;
+import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignNotFound;
+import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+import org.apache.fineract.infrastructure.jobs.service.JobName;
+import org.apache.fineract.infrastructure.jobs.service.SchedularWritePlatformService;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.infrastructure.campaigns.email.data.PreviewCampaignMessage;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignData;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignValidator;
+import org.apache.fineract.organisation.staff.domain.Staff;
+import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
+import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository;
+import org.apache.fineract.template.domain.TemplateRepository;
+import org.apache.fineract.template.service.TemplateMergeService;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import java.io.*;
+import java.util.*;
+
+@Service
+public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampaignWritePlatformService {
+
+
+ private final static Logger logger = LoggerFactory.getLogger(EmailCampaignWritePlatformCommandHandlerImpl.class);
+
+ private final PlatformSecurityContext context;
+
+ private final EmailCampaignRepository emailCampaignRepository;
+ private final EmailCampaignValidator emailCampaignValidator;
+ private final EmailCampaignReadPlatformService emailCampaignReadPlatformService;
+ private final ReportRepository reportRepository;
+ private final TemplateRepository templateRepository;
+ private final TemplateMergeService templateMergeService;
+ private final EmailMessageRepository emailMessageRepository;
+ private final ClientRepositoryWrapper clientRepositoryWrapper;
+ private final SchedularWritePlatformService schedularWritePlatformService;
+ private final ReadReportingService readReportingService;
+ private final GenericDataService genericDataService;
+ private final FromJsonHelper fromJsonHelper;
+ private final ReportParameterUsageRepository reportParameterUsageRepository;
+ private final LoanRepository loanRepository;
+ private final SavingsAccountRepository savingsAccountRepository;
+ private final EmailMessageJobEmailService emailMessageJobEmailService;
+
+
+
+
+ @Autowired
+ public EmailCampaignWritePlatformCommandHandlerImpl(final PlatformSecurityContext context, final EmailCampaignRepository emailCampaignRepository,
+ final EmailCampaignValidator emailCampaignValidator,final EmailCampaignReadPlatformService emailCampaignReadPlatformService,final ReportParameterUsageRepository reportParameterUsageRepository,
+ final ReportRepository reportRepository,final TemplateRepository templateRepository, final TemplateMergeService templateMergeService,
+ final EmailMessageRepository emailMessageRepository,final ClientRepositoryWrapper clientRepositoryWrapper,final SchedularWritePlatformService schedularWritePlatformService,
+ final ReadReportingService readReportingService, final GenericDataService genericDataService,final FromJsonHelper fromJsonHelper,
+ final LoanRepository loanRepository,final SavingsAccountRepository savingsAccountRepository,final EmailMessageJobEmailService emailMessageJobEmailService) {
+ this.context = context;
+ this.emailCampaignRepository = emailCampaignRepository;
+ this.emailCampaignValidator = emailCampaignValidator;
+ this.emailCampaignReadPlatformService = emailCampaignReadPlatformService;
+ this.reportRepository = reportRepository;
+ this.templateRepository = templateRepository;
+ this.templateMergeService = templateMergeService;
+ this.emailMessageRepository = emailMessageRepository;
+ this.clientRepositoryWrapper = clientRepositoryWrapper;
+ this.schedularWritePlatformService = schedularWritePlatformService;
+ this.readReportingService = readReportingService;
+ this.genericDataService = genericDataService;
+ this.fromJsonHelper = fromJsonHelper;
+ this.reportParameterUsageRepository = reportParameterUsageRepository;
+ this.loanRepository = loanRepository;
+ this.savingsAccountRepository = savingsAccountRepository;
+ this.emailMessageJobEmailService = emailMessageJobEmailService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult create(JsonCommand command) {
+
+ final AppUser currentUser = this.context.authenticatedUser();
+
+ this.emailCampaignValidator.validateCreate(command.json());
+
+ final Long businessRuleId = command.longValueOfParameterNamed(EmailCampaignValidator.businessRuleId);
+
+ final Report businessRule = this.reportRepository.findOne(businessRuleId);
+ if(businessRule == null){
+ throw new ReportNotFoundException(businessRuleId);
+ }
+
+ final Long reportId = command.longValueOfParameterNamed(EmailCampaignValidator.stretchyReportId);
+
+ final Report report = this.reportRepository.findOne(reportId);
+ if(report == null){
+ throw 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(),"");
+ }
+ }
+
+
+ EmailCampaign emailCampaign = EmailCampaign.instance(currentUser,businessRule,report,command);
+ emailCampaign.setStretchyReportParamMap(new Gson().toJson(stretchyReportParams));
+
+ this.emailCampaignRepository.save(emailCampaign);
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(emailCampaign.getId()) //
+ .build();
+ }
+ @Transactional
+ @Override
+ public CommandProcessingResult update(final Long resourceId, final JsonCommand command) {
+ try{
+ final AppUser currentUser = this.context.authenticatedUser();
+
+ this.emailCampaignValidator.validateForUpdate(command.json());
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(resourceId);
+
+ if(emailCampaign == null){ throw new EmailCampaignNotFound(resourceId);}
+ if(emailCampaign.isActive()){ throw new EmailCampaignMustBeClosedToEditException(emailCampaign.getId());}
+ final Map<String, Object> changes = emailCampaign.update(command);
+
+ if(changes.containsKey(EmailCampaignValidator.businessRuleId)){
+ final Long newValue = command.longValueOfParameterNamed(EmailCampaignValidator.businessRuleId);
+ final Report reportId = this.reportRepository.findOne(newValue);
+ if(reportId == null){ throw new ReportNotFoundException(newValue);}
+ emailCampaign.updateBusinessRuleId(reportId);
+
+ }
+
+ if(!changes.isEmpty()){
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+ }
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(resourceId) //
+ .with(changes) //
+ .build();
+ }catch(final DataIntegrityViolationException dve){
+ handleDataIntegrityIssues(command, dve);
+ return CommandProcessingResult.empty();
+ }
+
+ }
+ @Transactional
+ @Override
+ public CommandProcessingResult delete(final Long resourceId) {
+ final AppUser currentUser = this.context.authenticatedUser();
+
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(resourceId);
+
+ if(emailCampaign == null){ throw new EmailCampaignNotFound(resourceId);}
+ if(emailCampaign.isActive()){ throw new EmailCampaignMustBeClosedToBeDeletedException(emailCampaign.getId());}
+
+ /*
+ Do not delete but set a boolean is_visible to zero
+ */
+ emailCampaign.delete();
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+
+ return new CommandProcessingResultBuilder() //
+ .withEntityId(emailCampaign.getId()) //
+ .build();
+
+ }
+
+
+ private void insertDirectCampaignIntoEmailOutboundTable(final String emailParams, final String emailSubject,
+ final String messageTemplate,final String campaignName,final Long campaignId){
+ try{
+ HashMap<String,String> campaignParams = new ObjectMapper().readValue(emailParams, new TypeReference<HashMap<String,String>>(){});
+
+ HashMap<String,String> queryParamForRunReport = new ObjectMapper().readValue(emailParams, new TypeReference<HashMap<String,String>>(){});
+
+ List<HashMap<String,Object>> runReportObject = this.getRunReportByServiceImpl(campaignParams.get("reportName"),queryParamForRunReport);
+
+ if(runReportObject !=null){
+ for(HashMap<String,Object> entry : runReportObject){
+ String message = this.compileEmailTemplate(messageTemplate, campaignName, entry);
+ Integer clientId = (Integer)entry.get("id");
+ EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(campaignId);
+ Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId.longValue());
+ String emailAddress = client.emailAddress();
+
+ if(emailAddress !=null && isValidEmail(emailAddress)) {
+ EmailMessage emailMessage = EmailMessage.pendingEmail(null,client,null,emailCampaign,emailSubject,message,emailAddress,campaignName);
+ this.emailMessageRepository.save(emailMessage);
+ }
+ }
+ }
+ }catch(final IOException e){
+ // TODO throw something here
+ }
+
+ }
+
+ public static boolean isValidEmail(String email) {
+
+ boolean isValid = true;
+
+ try {
+
+ InternetAddress emailO = new InternetAddress(email);
+ emailO.validate();
+
+ } catch (AddressException ex) {
+
+ isValid = false;
+ }
+ return isValid;
+ }
+
+ @Override
+ @CronTarget(jobName = JobName.UPDATE_EMAIL_OUTBOUND_WITH_CAMPAIGN_MESSAGE)
+ public void storeTemplateMessageIntoEmailOutBoundTable() throws JobExecutionException {
+ final Collection<EmailCampaignData> emailCampaignDataCollection = this.emailCampaignReadPlatformService.retrieveAllScheduleActiveCampaign();
+ if(emailCampaignDataCollection != null){
+ for(EmailCampaignData emailCampaignData : emailCampaignDataCollection){
+ LocalDateTime tenantDateNow = tenantDateTime();
+ LocalDateTime nextTriggerDate = emailCampaignData.getNextTriggerDate().toLocalDateTime();
+
+ logger.info("tenant time " + tenantDateNow.toString() + " trigger time "+nextTriggerDate.toString());
+ if(nextTriggerDate.isBefore(tenantDateNow)){
+ insertDirectCampaignIntoEmailOutboundTable(emailCampaignData.getParamValue(),emailCampaignData.getEmailSubject(), emailCampaignData.getMessage(),emailCampaignData.getCampaignName(),emailCampaignData.getId());
+ this.updateTriggerDates(emailCampaignData.getId());
+ }
+ }
+ }
+ }
+
+ private void updateTriggerDates(Long campaignId){
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(campaignId);
+ if(emailCampaign == null){
+ throw new EmailCampaignNotFound(campaignId);
+ }
+ LocalDateTime nextTriggerDate = emailCampaign.getNextTriggerDate();
+ emailCampaign.setLastTriggerDate(nextTriggerDate.toDate());
+ //calculate new trigger date and insert into next trigger date
+
+ /**
+ * next run time has to be in the future if not calculate a new future date
+ */
+ LocalDate nextRuntime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), emailCampaign.getNextTriggerDate().toLocalDate(), nextTriggerDate.toLocalDate()) ;
+ if(nextRuntime.isBefore(DateUtils.getLocalDateOfTenant())){ // means next run time is in the past calculate a new future date
+ nextRuntime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), emailCampaign.getNextTriggerDate().toLocalDate(),DateUtils.getLocalDateOfTenant()) ;
+ }
+ final LocalDateTime getTime = emailCampaign.getRecurrenceStartDateTime();
+ final String dateString = nextRuntime.toString() + " " + getTime.getHourOfDay()+":"+getTime.getMinuteOfHour()+":"+getTime.getSecondOfMinute();
+ final DateTimeFormatter simpleDateFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
+ final LocalDateTime newTriggerDateWithTime = LocalDateTime.parse(dateString,simpleDateFormat);
+
+
+ emailCampaign.setNextTriggerDate(newTriggerDateWithTime.toDate());
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult activateEmailCampaign(Long campaignId, JsonCommand command) {
+ final AppUser currentUser = this.context.authenticatedUser();
+
+ this.emailCampaignValidator.validateActivation(command.json());
+
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(campaignId);
+
+ if(emailCampaign == null){
+ throw new EmailCampaignNotFound(campaignId);
+ }
+
+
+
+ final Locale locale = command.extractLocale();
+ final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
+ final LocalDate activationDate = command.localDateValueOfParameterNamed("activationDate");
+
+ emailCampaign.activate(currentUser, fmt, activationDate);
+
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+
+ if(emailCampaign.isDirect()){
+ insertDirectCampaignIntoEmailOutboundTable(emailCampaign.getParamValue(),emailCampaign.getEmailSubject(),emailCampaign.getEmailMessage(),emailCampaign.getCampaignName(), emailCampaign.getId());
+ }else {
+ if (emailCampaign.isSchedule()) {
+
+ /**
+ * if recurrence start date is in the future calculate
+ * next trigger date if not use recurrence start date us next trigger
+ * date when activating
+ */
+ LocalDate nextTriggerDate = null;
+ if(emailCampaign.getRecurrenceStartDateTime().isBefore(tenantDateTime())){
+ nextTriggerDate = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), emailCampaign.getRecurrenceStartDate(), DateUtils.getLocalDateOfTenant());
+ }else{
+ nextTriggerDate = emailCampaign.getRecurrenceStartDate();
+ }
+ // to get time of tenant
+ final LocalDateTime getTime = emailCampaign.getRecurrenceStartDateTime();
+
+ final String dateString = nextTriggerDate.toString() + " " + getTime.getHourOfDay()+":"+getTime.getMinuteOfHour()+":"+getTime.getSecondOfMinute();
+ final DateTimeFormatter simpleDateFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
+ final LocalDateTime nextTriggerDateWithTime = LocalDateTime.parse(dateString,simpleDateFormat);
+
+ emailCampaign.setNextTriggerDate(nextTriggerDateWithTime.toDate());
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+ }
+ }
+
+ /*
+ if campaign is direct insert campaign message into email outbound table
+ else if its a schedule create a job process for it
+ */
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(emailCampaign.getId()) //
+ .build();
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult closeEmailCampaign(Long campaignId, JsonCommand command) {
+
+ final AppUser currentUser = this.context.authenticatedUser();
+ this.emailCampaignValidator.validateClosedDate(command.json());
+
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(campaignId);
+ if(emailCampaign == null){
+ throw new EmailCampaignNotFound(campaignId);
+ }
+
+ final Locale locale = command.extractLocale();
+ final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
+ final LocalDate closureDate = command.localDateValueOfParameterNamed("closureDate");
+
+ emailCampaign.close(currentUser,fmt,closureDate);
+
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(emailCampaign.getId()) //
+ .build();
+ }
+
+ private String compileEmailTemplate(final String textMessageTemplate,final String campaignName , final Map<String, Object> emailParams) {
+ final MustacheFactory mf = new DefaultMustacheFactory();
+ final Mustache mustache = mf.compile(new StringReader(textMessageTemplate), campaignName);
+
+ final StringWriter stringWriter = new StringWriter();
+ mustache.execute(stringWriter, emailParams);
+
+ return stringWriter.toString();
+ }
+
+ private List<HashMap<String,Object>> getRunReportByServiceImpl(final String reportName,final Map<String, String> queryParams) throws IOException {
+ final String reportType ="report";
+
+ List<HashMap<String, Object>> resultList = new ArrayList<HashMap<String, Object>>();
+ final GenericResultsetData results = this.readReportingService.retrieveGenericResultSetForSmsEmailCampaign(reportName,
+ reportType, queryParams);
+ final String response = this.genericDataService.generateJsonFromGenericResultsetData(results);
+ resultList = new ObjectMapper().readValue(response, new TypeReference<List<HashMap<String,Object>>>(){});
+ //loop changes array date to string date
+ for(HashMap<String,Object> entry : resultList){
+ for(Map.Entry<String,Object> map: entry.entrySet()){
+ String key = map.getKey();
+ Object ob = map.getValue();
+ if(ob instanceof ArrayList && ((ArrayList) ob).size() == 3){
+ String changeArrayDateToStringDate = ((ArrayList) ob).get(2).toString() +"-"+((ArrayList) ob).get(1).toString() +"-"+((ArrayList) ob).get(0).toString();
+ entry.put(key,changeArrayDateToStringDate);
+ }
+ }
+ }
+ return resultList;
+ }
+
+ @Override
+ public PreviewCampaignMessage previewMessage(final JsonQuery query) {
+ PreviewCampaignMessage campaignMessage = null;
+ final AppUser currentUser = this.context.authenticatedUser();
+ this.emailCampaignValidator.validatePreviewMessage(query.json());
+ final String emailParams = this.fromJsonHelper.extractStringNamed("paramValue", query.parsedJson()) ;
+ final String textMessageTemplate = this.fromJsonHelper.extractStringNamed("emailMessage", query.parsedJson());
+
+ try{
+ HashMap<String,String> campaignParams = new ObjectMapper().readValue(emailParams, new TypeReference<HashMap<String,String>>(){});
+
+ HashMap<String,String> queryParamForRunReport = new ObjectMapper().readValue(emailParams, new TypeReference<HashMap<String,String>>(){});
+
+ List<HashMap<String,Object>> runReportObject = this.getRunReportByServiceImpl(campaignParams.get("reportName"),queryParamForRunReport);
+
+ if(runReportObject !=null){
+ for(HashMap<String,Object> entry : runReportObject){
+ // add string object to campaignParam object
+ String textMessage = this.compileEmailTemplate(textMessageTemplate,"EmailCampaign", entry);
+ if(!textMessage.isEmpty()) {
+ final Integer totalMessage = runReportObject.size();
+ campaignMessage = new PreviewCampaignMessage(textMessage,totalMessage);
+ break;
+ }
+ }
+ }
+ }catch(final IOException e){
+ // TODO throw something here
+ }
+
+ return campaignMessage;
+
+ }
+ @Transactional
+ @Override
+ public CommandProcessingResult reactivateEmailCampaign(final Long campaignId, JsonCommand command) {
+
+ this.emailCampaignValidator.validateActivation(command.json());
+
+ final AppUser currentUser = this.context.authenticatedUser();
+
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(campaignId);
+
+ if(emailCampaign == null){ throw new EmailCampaignNotFound(campaignId);}
+
+ final Locale locale = command.extractLocale();
+ final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
+ final LocalDate reactivationDate = command.localDateValueOfParameterNamed("activationDate");
+ emailCampaign.reactivate(currentUser,fmt,reactivationDate);
+ if (emailCampaign.isSchedule()) {
+
+ /**
+ * if recurrence start date is in the future calculate
+ * next trigger date if not use recurrence start date us next trigger date when activating
+ */
+ LocalDate nextTriggerDate = null;
+ if(emailCampaign.getRecurrenceStartDateTime().isBefore(tenantDateTime())){
+ nextTriggerDate = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), emailCampaign.getRecurrenceStartDate(), DateUtils.getLocalDateOfTenant());
+ }else{
+ nextTriggerDate = emailCampaign.getRecurrenceStartDate();
+ }
+ // to get time of tenant
+ final LocalDateTime getTime = emailCampaign.getRecurrenceStartDateTime();
+
+ final String dateString = nextTriggerDate.toString() + " " + getTime.getHourOfDay()+":"+getTime.getMinuteOfHour()+":"+getTime.getSecondOfMinute();
+ final DateTimeFormatter simpleDateFormat = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
+ final LocalDateTime nextTriggerDateWithTime = LocalDateTime.parse(dateString,simpleDateFormat);
+
+ emailCampaign.setNextTriggerDate(nextTriggerDateWithTime.toDate());
+ this.emailCampaignRepository.saveAndFlush(emailCampaign);
+ }
+
+
+
+ return new CommandProcessingResultBuilder() //
+ .withEntityId(emailCampaign.getId()) //
+ .build();
+
+ }
+
+ private void handleDataIntegrityIssues(@SuppressWarnings("unused") final JsonCommand command, final DataIntegrityViolationException dve) {
+ final Throwable realCause = dve.getMostSpecificCause();
+
+ throw new PlatformDataIntegrityException("error.msg.email.campaign.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource: " + realCause.getMessage());
+ }
+
+ private LocalDateTime tenantDateTime(){
+ LocalDateTime today = new LocalDateTime();
+ final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
+
+ if (tenant != null) {
+ final DateTimeZone zone = DateTimeZone.forID(tenant.getTimezoneId());
+ if (zone != null) {
+ today = new LocalDateTime(zone);
+ }
+ }
+ return today;
+ }
+
+ @Override
+ @CronTarget(jobName = JobName.EXECUTE_EMAIL)
+ public void sendEmailMessage() throws JobExecutionException {
+ if (IPv4Helper.applicationIsNotRunningOnLocalMachine()){ //remove when testing locally
+ final List<EmailMessage> emailMessages = this.emailMessageRepository.findByStatusType(EmailMessageStatusType.PENDING.getValue()); //retrieve all pending message
+
+ for(final EmailMessage emailMessage : emailMessages) {
+
+
+ if (isValidEmail(emailMessage.getEmailAddress())) {
+
+
+ final EmailCampaign emailCampaign = this.emailCampaignRepository.findOne(emailMessage.getEmailCampaign().getId()); //
+
+ final ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat = ScheduledEmailAttachmentFileFormat.instance(emailCampaign.getEmailAttachmentFileFormat());
+
+ final List<File> attachmentList = new ArrayList<>();
+
+ final StringBuilder errorLog = new StringBuilder();
+
+ //check if email attachment format exist
+ if (emailAttachmentFileFormat != null && Arrays.asList(ScheduledEmailAttachmentFileFormat.validValues()).
+ contains(emailAttachmentFileFormat.getId())) {
+
+ final Report stretchyReport = emailCampaign.getStretchyReport();
+
+ final String reportName = (stretchyReport != null) ? stretchyReport.getReportName() : null;
+
+ final HashMap<String, String> reportStretchyParams = this.validateStretchyReportParamMap(emailCampaign.getStretchyReportParamMap());
+
+ // there is a probability that a client has one or more loans or savings therefore we need to send two or more attachments
+ if (reportStretchyParams.containsKey("selectLoan") || reportStretchyParams.containsKey("loanId")) {
+ //get all ids of the client loans
+ if (emailMessage.getClient() != null) {
+
+ final List<Loan> loans = this.loanRepository.findLoanByClientId(emailMessage.getClient().getId());
+
+ HashMap<String, String> reportParams = this.replaceStretchyParamsWithActualClientParams(reportStretchyParams, emailMessage.getClient());
+
+ for (final Loan loan : loans) {
+ if (loan.isOpen()) { // only send attachment for active loan
+
+ if (reportStretchyParams.containsKey("selectLoan")) {
+
+ reportParams.put("SelectLoan", loan.getId().toString());
+
+ } else if (reportStretchyParams.containsKey("loanId")) {
+
+ reportParams.put("loanId", loan.getId().toString());
+ }
+ File file = this.generateAttachments(emailCampaign, emailAttachmentFileFormat, reportParams, reportName, errorLog);
+
+ if (file != null) {
+ attachmentList.add(file);
+ } else {
+ errorLog.append(reportParams.toString());
+ }
+ }
+ }
+
+ }
+ } else if (reportStretchyParams.containsKey("savingId")) {
+ if (emailMessage.getClient() != null) {
+
+ final List<SavingsAccount> savingsAccounts = this.savingsAccountRepository.findSavingAccountByClientId(emailMessage.getClient().getId());
+
+ HashMap<String, String> reportParams = this.replaceStretchyParamsWithActualClientParams(reportStretchyParams, emailMessage.getClient());
+
+ for (final SavingsAccount savingsAccount : savingsAccounts) {
+
+ if (savingsAccount.isActive()) {
+
+ reportParams.put("savingId", savingsAccount.getId().toString());
+
+ File file = this.generateAttachments(emailCampaign, emailAttachmentFileFormat, reportParams, reportName, errorLog);
+
+ if (file != null) {
+ attachmentList.add(file);
+ } else {
+ errorLog.append(reportParams.toString());
+ }
+ }
+ }
+ }
+ } else {
+ if (emailMessage.getClient() != null) {
+
+ HashMap<String, String> reportParams = this.replaceStretchyParamsWithActualClientParams(reportStretchyParams, emailMessage.getClient());
+
+ File file = this.generateAttachments(emailCampaign, emailAttachmentFileFormat, reportParams, reportName, errorLog);
+
+ if (file != null) {
+ attachmentList.add(file);
+ } else {
+ errorLog.append(reportParams.toString());
+ }
+ }
+ }
+
+ }
+
+ 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
+
+ this.emailMessageJobEmailService.sendEmailWithAttachment(emailMessageWithAttachmentData);
+
+ emailMessage.setStatusType(EmailMessageStatusType.SENT.getValue());
+
+ this.emailMessageRepository.save(emailMessage);
+ } else {
+ emailMessage.updateErrorMessage(errorLog.toString());
+
+ emailMessage.setStatusType(EmailMessageStatusType.FAILED.getValue());
+
+ this.emailMessageRepository.save(emailMessage);
+ }
+ }
+ }
+
+ }
+
+
+ }
+
+ /**
+ * This generates the the report and converts it to a file by passing the parameters below
+ * @param emailCampaign
+ * @param emailAttachmentFileFormat
+ * @param reportParams
+ * @param reportName
+ * @param errorLog
+ * @return
+ */
+ private File generateAttachments(final EmailCampaign emailCampaign, final ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat,
+ final Map<String, String> reportParams, final String reportName, final StringBuilder errorLog){
+
+ try{
+ final ByteArrayOutputStream byteArrayOutputStream = this.readReportingService.generatePentahoReportAsOutputStream(reportName,
+ emailAttachmentFileFormat.getValue(), reportParams, null, emailCampaign.getApprovedBy(), errorLog);
+
+ final String fileLocation = FileSystemContentRepository.FINERACT_BASE_DIR + File.separator + "";
+ final String fileNameWithoutExtension = fileLocation + File.separator + reportName;
+
+ // check if file directory exists, if not create directory
+ if (!new File(fileLocation).isDirectory()) {
+ new File(fileLocation).mkdirs();
+ }
+
+ if (byteArrayOutputStream.size() == 0) {
+ errorLog.append("Pentaho report processing failed, empty output stream created");
+ }
+ else if (errorLog.length() == 0 && (byteArrayOutputStream.size() > 0)) {
+ final String fileName = fileNameWithoutExtension + "." + emailAttachmentFileFormat.getValue();
+
+ final File file = new File(fileName);
+ final FileOutputStream outputStream = new FileOutputStream(file);
+ byteArrayOutputStream.writeTo(outputStream);
+
+ return file;
+ }
+
+ }catch(IOException | PlatformDataIntegrityException e){
+ errorLog.append("The ReportMailingJobWritePlatformServiceImpl.executeReportMailingJobs threw an IOException "
+ + "exception: " + e.getMessage() + " ---------- ");
+ }
+ return null;
+ }
+
+ /**
+ * This matches the the actual values to the key in the report stretchy parameters map
+ * @param stretchyParams
+ * @param client
+ * @return
+ */
+ private HashMap<String,String> replaceStretchyParamsWithActualClientParams(final HashMap<String,String> stretchyParams,final Client client){
+
+ HashMap<String,String> actualParams = new HashMap<>();
+
+ for (Map.Entry<String, String> entry : stretchyParams.entrySet()) {
+ if(entry.getKey().equals("selectOffice")){
+ //most at times the reports are run by picking the office of the staff Id
+ if(client.getStaff() !=null){
+ actualParams.put(entry.getKey(),client.getStaff().officeId().toString());
+ }else {
+ actualParams.put(entry.getKey(), client.getOffice().getId().toString());
+ }
+
+ }else if(entry.getKey().equals("selectClient")){
+
+ actualParams.put(entry.getKey(),client.getId().toString());
+
+ }else if(entry.getKey().equals("selectLoanofficer")){
+
+ actualParams.put(entry.getKey(),client.getStaff().getId().toString());
+
+ }else if(entry.getKey().equals("environementUrl")){
+
+ actualParams.put(entry.getKey(),entry.getKey());
+ }
+ }
+ return actualParams;
+ }
+
+
+ private HashMap<String,String> validateStretchyReportParamMap(final String stretchyParams){
+
+ HashMap<String,String> stretchyReportParamHashMap = new HashMap<>();
+
+ if (!StringUtils.isEmpty(stretchyParams)) {
+ try {
+ stretchyReportParamHashMap = new ObjectMapper().readValue(stretchyParams, new TypeReference<HashMap<String,String>>(){});
+ }
+
+ catch(Exception e) {
+ stretchyReportParamHashMap = null;
+ }
+ }
+
+ return stretchyReportParamHashMap;
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..a86262e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java
@@ -0,0 +1,47 @@
+/**
+ * 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 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.infrastructure.campaigns.email.data.PreviewCampaignMessage;
+
+public interface EmailCampaignWritePlatformService {
+
+ CommandProcessingResult create(JsonCommand command);
+
+ CommandProcessingResult update(Long resourceId, JsonCommand command);
+
+ CommandProcessingResult delete(Long resourceId);
+
+ CommandProcessingResult activateEmailCampaign(Long campaignId, JsonCommand command);
+
+ CommandProcessingResult closeEmailCampaign(Long campaignId, JsonCommand command);
+
+ CommandProcessingResult reactivateEmailCampaign(Long campaignId, JsonCommand command);
+
+ void storeTemplateMessageIntoEmailOutBoundTable() throws JobExecutionException;
+
+ PreviewCampaignMessage previewMessage(JsonQuery query);
+
+ void sendEmailMessage() throws JobExecutionException;
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java
new file mode 100644
index 0000000..cc704e6
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java
@@ -0,0 +1,30 @@
+/**
+ * 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 org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData;
+
+import java.util.Collection;
+
+public interface EmailConfigurationReadPlatformService {
+
+ Collection<EmailConfigurationData> retrieveAll();
+
+ EmailConfigurationData retrieveOne(String name);
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java
new file mode 100644
index 0000000..394e082
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java
@@ -0,0 +1,99 @@
+/**
+ * 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 org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.campaigns.email.exception.EmailConfigurationNotFoundException;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
+
+@Service
+public class EmailConfigurationReadPlatformServiceImpl implements EmailConfigurationReadPlatformService {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final EmailConfigurationRowMapper emailConfigurationRowMapper;
+
+ @Autowired
+ public EmailConfigurationReadPlatformServiceImpl(final RoutingDataSource dataSource) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ this.emailConfigurationRowMapper = new EmailConfigurationRowMapper();
+
+ }
+
+ private static final class EmailConfigurationRowMapper implements RowMapper<EmailConfigurationData> {
+
+ final String schema;
+
+ public EmailConfigurationRowMapper() {
+ final StringBuilder sql = new StringBuilder(300);
+ sql.append("cnf.id as id, ");
+ sql.append("cnf.name as name, ");
+ sql.append("cnf.value as value ");
+ sql.append("from scheduled_email_configuration cnf");
+
+ this.schema = sql.toString();
+ }
+
+ public String schema() {
+ return this.schema;
+ }
+
+ @Override
+ public EmailConfigurationData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
+
+ final Long id = JdbcSupport.getLong(rs, "id");
+ final String name = rs.getString("name");
+ final String value = rs.getString("value");
+
+ return EmailConfigurationData.instance(id, name, value);
+ }
+
+ }
+
+ @Override
+ public Collection<EmailConfigurationData> retrieveAll() {
+ final String sql = "select " + this.emailConfigurationRowMapper.schema();
+
+ return this.jdbcTemplate.query(sql, this.emailConfigurationRowMapper, new Object[] {});
+ }
+
+ @Override
+ public EmailConfigurationData retrieveOne(String name) {
+ try {
+ final String sql = "select " + this.emailConfigurationRowMapper.schema() + " where cnf.name = ?";
+
+ return this.jdbcTemplate.queryForObject(sql, this.emailConfigurationRowMapper, name);
+ }
+
+ catch(final EmptyResultDataAccessException e) {
+
+ throw new EmailConfigurationNotFoundException(name);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java
new file mode 100644
index 0000000..3afa8d3
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java
@@ -0,0 +1,28 @@
+/**
+ * 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 org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+
+public interface EmailConfigurationWritePlatformService {
+
+ CommandProcessingResult update(JsonCommand command);
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java
new file mode 100644
index 0000000..508e283
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java
@@ -0,0 +1,89 @@
+/**
+ * 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 com.google.gson.JsonElement;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationValidator;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfiguration;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository;
+import org.apache.fineract.infrastructure.campaigns.email.exception.EmailConfigurationSMTPUsernameNotValid;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+@Service
+public class EmailConfigurationWritePlatformServiceImpl implements EmailConfigurationWritePlatformService {
+
+ private final PlatformSecurityContext context;
+ private final EmailConfigurationRepository repository;
+ private final EmailConfigurationValidator emailConfigurationValidator;
+
+ @Autowired
+ public EmailConfigurationWritePlatformServiceImpl(final PlatformSecurityContext context, final EmailConfigurationRepository repository,
+ final EmailConfigurationValidator emailConfigurationValidator) {
+ this.context = context;
+ this.repository = repository;
+ this.emailConfigurationValidator = emailConfigurationValidator;
+ }
+
+ @Override
+ public CommandProcessingResult update(final JsonCommand command) {
+
+ final AppUser currentUser = this.context.authenticatedUser();
+
+ this.emailConfigurationValidator.validateUpdateConfiguration(command.json());
+ final String smtpUsername = command.stringValueOfParameterNamed("SMTP_USERNAME");
+
+ if(!this.emailConfigurationValidator.isValidEmail(smtpUsername)){
+ throw new EmailConfigurationSMTPUsernameNotValid(smtpUsername);
+ }
+
+
+ final Map<String,Object> changes = new HashMap<>(4);
+
+ Collection<EmailConfiguration> configurations = this.repository.findAll();
+ /**
+ *Default SMTP configuration added to flyway
+ */
+ for (EmailConfiguration config : configurations) {
+ if(config.getName() !=null){
+ String value = command.stringValueOfParameterNamed(config.getName());
+ config.setValue(value); changes.put(config.getName(),value);
+ this.repository.saveAndFlush(config);
+ }
+ }
+
+ return new CommandProcessingResultBuilder() //
+ .with(changes)
+ .build();
+
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java
new file mode 100644
index 0000000..37b193b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java
@@ -0,0 +1,28 @@
+/**
+ * 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 org.apache.fineract.infrastructure.campaigns.email.data.EmailMessageWithAttachmentData;
+
+public interface EmailMessageJobEmailService {
+
+ void sendEmailWithAttachment(EmailMessageWithAttachmentData emailMessageWithAttachmentData);
+
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..c8854a2
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java
@@ -0,0 +1,110 @@
+/**
+ * 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 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.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Service;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.io.File;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+@Service
+public class EmailMessageJobEmailServiceImpl implements EmailMessageJobEmailService {
+
+ private EmailConfigurationRepository emailConfigurationRepository;
+
+ @Autowired
+ private EmailMessageJobEmailServiceImpl(final EmailConfigurationRepository emailConfigurationRepository) {
+ this.emailConfigurationRepository = emailConfigurationRepository;
+ }
+
+ @Override
+ public void sendEmailWithAttachment(EmailMessageWithAttachmentData emailMessageWithAttachmentData) {
+ 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());
+
+ MimeMessage mimeMessage = javaMailSenderImpl.createMimeMessage();
+
+ // use the true flag to indicate you need a multipart message
+ MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
+
+ mimeMessageHelper.setTo(emailMessageWithAttachmentData.getTo());
+ mimeMessageHelper.setText(emailMessageWithAttachmentData.getText());
+ mimeMessageHelper.setSubject(emailMessageWithAttachmentData.getSubject());
+ final List<File> attachments = emailMessageWithAttachmentData.getAttachments();
+ if(attachments !=null && attachments.size() > 0){
+ for(final File attachment : attachments){
+ if(attachment !=null){
+ mimeMessageHelper.addAttachment(attachment.getName(),attachment);
+ }
+ }
+ }
+
+ javaMailSenderImpl.send(mimeMessage);
+
+ }catch(MessagingException e){
+
+ }
+
+ }
+
+
+ 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());
+
+ return properties;
+ }
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java
new file mode 100644
index 0000000..6348023
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java
@@ -0,0 +1,45 @@
+/**
+ * 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 org.apache.fineract.infrastructure.core.service.Page;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailData;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+public interface EmailReadPlatformService {
+
+ Collection<EmailData> retrieveAll();
+
+ EmailData retrieveOne(Long resourceId);
+
+ Collection<EmailData> retrieveAllPending(Integer limit);
+
+ Collection<EmailData> retrieveAllSent(Integer limit);
+
+ Collection<EmailData> retrieveAllDelivered(Integer limit);
+
+ Collection<EmailData> retrieveAllFailed(Integer limit);
+
+ Page<EmailData> retrieveEmailByStatus(Integer limit, Integer status, Date dateFrom, Date dateTo);
+
+ List<Long> retrieveExternalIdsOfAllSent(Integer limit);
+}
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java
new file mode 100644
index 0000000..98d4dfe
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java
@@ -0,0 +1,199 @@
+/**
+ * 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 org.joda.time.LocalDate;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
+import org.apache.fineract.infrastructure.core.service.Page;
+import org.apache.fineract.infrastructure.core.service.PaginationHelper;
+import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.campaigns.email.exception.EmailNotFoundException;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailData;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageEnumerations;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageStatusType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class EmailReadPlatformServiceImpl implements EmailReadPlatformService {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final EmailMapper emailRowMapper = new EmailMapper();
+ private final PaginationHelper<EmailData> paginationHelper = new PaginationHelper<>();
+
+
+ @Autowired
+ public EmailReadPlatformServiceImpl(final RoutingDataSource dataSource) {
+ this.jdbcTemplate = new JdbcTemplate(dataSource);
+ }
+
+ private static final class EmailMapper implements RowMapper<EmailData> {
+
+ final String schema;
+
+ public EmailMapper() {
+ final StringBuilder sql = new StringBuilder(300);
+ sql.append(" emo.id as id, ");
+ sql.append("emo.group_id as groupId, ");
+ sql.append("emo.client_id as clientId, ");
+ sql.append("emo.staff_id as staffId, ");
+ sql.append("emo.campaign_name as campaignName, ");
+ sql.append("emo.status_enum as statusId, ");
+ sql.append("emo.email_address as emailAddress, ");
+ sql.append("emo.submittedon_date as sentDate, ");
+ sql.append("emo.email_subject as emailSubject, ");
+ sql.append("emo.message as message, ");
+ sql.append("emo.error_message as errorMessage ");
+ sql.append("from " + tableName() + " emo");
+
+ this.schema = sql.toString();
+ }
+
+ public String schema() {
+ return this.schema;
+ }
+
+ public String tableName() {
+ return "scheduled_email_messages_outbound";
+ }
+
+ @Override
+ public EmailData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
+
+ final Long id = JdbcSupport.getLong(rs, "id");
+ final Long groupId = JdbcSupport.getLong(rs, "groupId");
+ final Long clientId = JdbcSupport.getLong(rs, "clientId");
+ final Long staffId = JdbcSupport.getLong(rs, "staffId");
+
+ final String emailAddress = rs.getString("emailAddress");
+ final String emailSubject = rs.getString("emailSubject");
+ final String message = rs.getString("message");
+ final String campaignName = rs.getString("campaignName");
+
+ final Integer statusId = JdbcSupport.getInteger(rs, "statusId");
+ final LocalDate sentDate = JdbcSupport.getLocalDate(rs, "sentDate");
+ final String errorMessage = rs.getString("errorMessage");
+
+ final EnumOptionData status = EmailMessageEnumerations.status(statusId);
+
+ return EmailData.instance(id,groupId, clientId, staffId, status, emailAddress, emailSubject,
+ message,null,null,null,null,null,campaignName,sentDate,errorMessage);
+ }
+ }
+
+ @Override
+ public Collection<EmailData> retrieveAll() {
+
+ final String sql = "select " + this.emailRowMapper.schema();
+
+ return this.jdbcTemplate.query(sql, this.emailRowMapper, new Object[] {});
+ }
+
+ @Override
+ public EmailData retrieveOne(final Long resourceId) {
+ try {
+ final String sql = "select " + this.emailRowMapper.schema() + " where emo.id = ?";
+
+ return this.jdbcTemplate.queryForObject(sql, this.emailRowMapper, resourceId);
+ } catch (final EmptyResultDataAccessException e) {
+ throw new EmailNotFoundException(resourceId);
+ }
+ }
+
+ @Override
+ public Collection<EmailData> retrieveAllPending(final Integer limit) {
+ final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : "";
+ final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = "
+ + EmailMessageStatusType.PENDING.getValue() + sqlPlusLimit;
+
+ return this.jdbcTemplate.query(sql, this.emailRowMapper, new Object[] {});
+ }
+
+ @Override
+ public Collection<EmailData> retrieveAllSent(final Integer limit) {
+ final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : "";
+ final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = "
+ + EmailMessageStatusType.SENT.getValue() + sqlPlusLimit;
+
+ return this.jdbcTemplate.query(sql, this.emailRowMapper, new Object[] {});
+ }
+
+ @Override
+ public List<Long> retrieveExternalIdsOfAllSent(final Integer limit) {
+ final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : "";
+ final String sql = "select external_id from " + this.emailRowMapper.tableName() + " where status_enum = "
+ + EmailMessageStatusType.SENT.getValue() + sqlPlusLimit;
+
+ return this.jdbcTemplate.queryForList(sql, Long.class);
+ }
+
+ @Override
+ public Collection<EmailData> retrieveAllDelivered(final Integer limit) {
+ final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : "";
+ final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = "
+ + EmailMessageStatusType.DELIVERED.getValue() + sqlPlusLimit;
+
+ return this.jdbcTemplate.query(sql, this.emailRowMapper, new Object[] {});
+ }
+
+ @Override
+ public Collection<EmailData> retrieveAllFailed(final Integer limit) {
+ final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : "";
+ final String sql = "select " + this.emailRowMapper.schema() + " where emo.status_enum = "
+ + EmailMessageStatusType.FAILED.getValue() + sqlPlusLimit;
+
+ return this.jdbcTemplate.query(sql, this.emailRowMapper, new Object[] {});
+ }
+
+ @Override
+ public Page<EmailData> retrieveEmailByStatus(final Integer limit, final Integer status,final Date dateFrom, final Date dateTo) {
+ final StringBuilder sqlBuilder = new StringBuilder(200);
+ sqlBuilder.append("select SQL_CALC_FOUND_ROWS ");
+ sqlBuilder.append(this.emailRowMapper.schema());
+ if(status !=null){
+ sqlBuilder.append(" where emo.status_enum= ? ");
+ }
+ String fromDateString = null;
+ String toDateString = null;
+ if(dateFrom !=null && dateTo !=null){
+ final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+ fromDateString = df.format(dateFrom);
+ toDateString = df.format(dateTo);
+ sqlBuilder.append(" and emo.submittedon_date >= ? and emo.submittedon_date <= ? ");
+ }
+ final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : "";
+ if(!sqlPlusLimit.isEmpty()){
+ sqlBuilder.append(sqlPlusLimit);
+ }
+ final String sqlCountRows = "SELECT FOUND_ROWS()";
+ return this.paginationHelper.fetchPage(this.jdbcTemplate,sqlCountRows,sqlBuilder.toString(),new Object[]{status,fromDateString,toDateString},this.emailRowMapper);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java
new file mode 100644
index 0000000..233427c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java
@@ -0,0 +1,31 @@
+/**
+ * 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 org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+
+public interface EmailWritePlatformService {
+
+ CommandProcessingResult create(JsonCommand command);
+
+ CommandProcessingResult update(Long resourceId, JsonCommand command);
+
+ CommandProcessingResult delete(Long resourceId);
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java
new file mode 100644
index 0000000..9e23bc6
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java
@@ -0,0 +1,134 @@
+/**
+ * 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 org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
+import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.campaigns.email.data.EmailDataValidator;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessage;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageAssembler;
+import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Map;
+
+@Service
+public class EmailWritePlatformServiceJpaRepositoryImpl implements EmailWritePlatformService {
+
+ private final static Logger logger = LoggerFactory.getLogger(EmailWritePlatformServiceJpaRepositoryImpl.class);
+
+ private final EmailMessageAssembler assembler;
+ private final EmailMessageRepository repository;
+ private final EmailDataValidator validator;
+
+ @Autowired
+ public EmailWritePlatformServiceJpaRepositoryImpl(final EmailMessageAssembler assembler, final EmailMessageRepository repository,
+ final EmailDataValidator validator) {
+ this.assembler = assembler;
+ this.repository = repository;
+ this.validator = validator;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult create(final JsonCommand command) {
+
+ try {
+ this.validator.validateCreateRequest(command);
+
+ final EmailMessage message = this.assembler.assembleFromJson(command);
+
+ // TODO: at this point we also want to fire off request using third
+ // party service to send Email.
+ // TODO: decision to be made on wheter we 'wait' for response or use
+ // 'future/promise' to capture response and update the EmailMessage
+ // table
+ this.repository.save(message);
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(message.getId()) //
+ .build();
+ } catch (final DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(command, dve);
+ return CommandProcessingResult.empty();
+ }
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult update(final Long resourceId, final JsonCommand command) {
+
+ try {
+ this.validator.validateUpdateRequest(command);
+
+ final EmailMessage message = this.assembler.assembleFromResourceId(resourceId);
+ final Map<String, Object> changes = message.update(command);
+ if (!changes.isEmpty()) {
+ this.repository.save(message);
+ }
+
+ return new CommandProcessingResultBuilder() //
+ .withCommandId(command.commandId()) //
+ .withEntityId(resourceId) //
+ .with(changes) //
+ .build();
+ } catch (final DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(command, dve);
+ return CommandProcessingResult.empty();
+ }
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult delete(final Long resourceId) {
+
+ try {
+ final EmailMessage message = this.assembler.assembleFromResourceId(resourceId);
+ this.repository.delete(message);
+ this.repository.flush();
+ } catch (final DataIntegrityViolationException dve) {
+ handleDataIntegrityIssues(null, dve);
+ return CommandProcessingResult.empty();
+ }
+ return new CommandProcessingResultBuilder().withEntityId(resourceId).build();
+ }
+
+ /*
+ * Guaranteed to throw an exception no matter what the data integrity issue
+ * is.
+ */
+ private void handleDataIntegrityIssues(@SuppressWarnings("unused") final JsonCommand command, final DataIntegrityViolationException dve) {
+ final Throwable realCause = dve.getMostSpecificCause();
+
+ if (realCause.getMessage().contains("email_address")) { throw new PlatformDataIntegrityException("error.msg.email.no.email.address.exists",
+ "The group, client or staff provided has no email address.", "id"); }
+
+ logger.error(dve.getMessage(), dve);
+ throw new PlatformDataIntegrityException("error.msg.email.unknown.data.integrity.issue",
+ "Unknown data integrity issue with resource: " + realCause.getMessage());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java
index 54e714c..8de1bbf 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java
@@ -126,7 +126,7 @@ public class SmsCampaignApiResource {
@GET
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveAllCampaigns(@QueryParam("sqlSearch") final String sqlSearch,
+ public String retrieveAllEmails(@QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder, @Context final UriInfo uriInfo) {
this.platformSecurityContext.authenticatedUser().validateHasReadPermission(SmsCampaignConstants.RESOURCE_NAME);
http://git-wip-us.apache.org/repos/asf/fineract/blob/c338c175/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
index 0084ed6..8c9dd30 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
@@ -514,7 +514,7 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
final String reportType = "report";
List<HashMap<String, Object>> resultList = new ArrayList<>();
- final GenericResultsetData results = this.readReportingService.retrieveGenericResultSetForSmsCampaign(reportName, reportType,
+ final GenericResultsetData results = this.readReportingService.retrieveGenericResultSetForSmsEmailCampaign(reportName, reportType,
queryParams);
try {