Repository: incubator-fineract
Updated Branches:
  refs/heads/develop f8f3f743c -> 47f21bc8d


http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java
new file mode 100644
index 0000000..afec180
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobReadPlatformServiceImpl.java
@@ -0,0 +1,203 @@
+/**
+ * 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.reportmailingjob.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+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.core.service.SearchParameters;
+import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobData;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobTimelineData;
+import 
org.apache.fineract.infrastructure.reportmailingjob.exception.ReportMailingJobNotFoundException;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+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;
+
+@Service
+public class ReportMailingJobReadPlatformServiceImpl implements 
ReportMailingJobReadPlatformService {
+    private final JdbcTemplate jdbcTemplate;
+    
+    @Autowired
+    public ReportMailingJobReadPlatformServiceImpl(final RoutingDataSource 
dataSource) {
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+    }
+
+    @Override
+    public Page<ReportMailingJobData> retrieveAllReportMailingJobs(final 
SearchParameters searchParameters) {
+        final StringBuilder sqlStringBuilder = new StringBuilder(200);
+        final List<Object> queryParameters = new ArrayList<>();
+        final ReportMailingJobMapper mapper = new ReportMailingJobMapper();
+        final PaginationHelper<ReportMailingJobData> paginationHelper = new 
PaginationHelper<>();
+        
+        sqlStringBuilder.append("select SQL_CALC_FOUND_ROWS ");
+        sqlStringBuilder.append(mapper.ReportMailingJobSchema());
+        sqlStringBuilder.append(" where rmj.is_deleted = 0");
+        
+        if (searchParameters.isOrderByRequested()) {
+            sqlStringBuilder.append(" order by 
").append(searchParameters.getOrderBy());
+
+            if (searchParameters.isSortOrderProvided()) {
+                sqlStringBuilder.append(" 
").append(searchParameters.getSortOrder());
+            }
+        } else {
+            sqlStringBuilder.append(" order by rmj.name ");
+        }
+
+        if (searchParameters.isLimited()) {
+            sqlStringBuilder.append(" limit 
").append(searchParameters.getLimit());
+            
+            if (searchParameters.isOffset()) {
+                sqlStringBuilder.append(" offset 
").append(searchParameters.getOffset());
+            }
+        }
+        
+        return paginationHelper.fetchPage(this.jdbcTemplate, "SELECT 
FOUND_ROWS()", sqlStringBuilder.toString(), 
+                queryParameters.toArray(), mapper);
+    }
+    
+    @Override
+    public Collection<ReportMailingJobData> 
retrieveAllActiveReportMailingJobs() {
+        final ReportMailingJobMapper mapper = new ReportMailingJobMapper();
+        final String sql = "select " + mapper.ReportMailingJobSchema() + " 
where rmj.is_deleted = 0 and is_active = 1"
+                + " order by rmj.name";
+        
+        return this.jdbcTemplate.query(sql, mapper, new Object[] {});
+    }
+
+    @Override
+    public ReportMailingJobData retrieveReportMailingJob(final Long 
reportMailingJobId) {
+        try {
+            final ReportMailingJobMapper mapper = new ReportMailingJobMapper();
+            final String sql = "select " + mapper.ReportMailingJobSchema() + " 
where rmj.id = ? and rmj.is_deleted = 0";
+            
+            return this.jdbcTemplate.queryForObject(sql, mapper, new Object[] 
{ reportMailingJobId });
+        }
+        
+        catch (final EmptyResultDataAccessException ex) {
+            throw new ReportMailingJobNotFoundException(reportMailingJobId);
+        }
+    }
+
+    @Override
+    public ReportMailingJobData retrieveReportMailingJobEnumOptions() {
+        final List<EnumOptionData> emailAttachmentFileFormatOptions = 
ReportMailingJobEmailAttachmentFileFormat.validOptions();
+        final List<EnumOptionData> stretchyReportParamDateOptions = 
ReportMailingJobStretchyReportParamDateOption.validOptions();
+        
+        return 
ReportMailingJobData.newInstance(emailAttachmentFileFormatOptions, 
stretchyReportParamDateOptions);
+    }
+    
+    private static final class ReportMailingJobMapper implements 
RowMapper<ReportMailingJobData> {
+        public String ReportMailingJobSchema() {
+            return "rmj.id, rmj.name, rmj.description, rmj.start_datetime as 
startDateTime, rmj.recurrence, rmj.created_date as createdOnDate, "
+                    + "cbu.username as createdByUsername, cbu.firstname as 
createdByFirstname, cbu.lastname as createdByLastname, "
+                    + "rmj.lastmodified_date as updatedOnDate, "
+                    + "mbu.username as updatedByUsername, mbu.firstname as 
updatedByFirstname, mbu.lastname as updatedByLastname, "
+                    + "rmj.email_recipients as emailRecipients, "
+                    + "rmj.email_subject as emailSubject, rmj.email_message as 
emailMessage, "
+                    + "rmj.email_attachment_file_format as 
emailAttachmentFileFormat, "
+                    + "rmj.stretchy_report_param_map as 
stretchyReportParamMap, rmj.previous_run_datetime as previousRunDateTime, "
+                    + "rmj.next_run_datetime as nextRunDateTime, 
rmj.previous_run_status as previousRunStatus, "
+                    + "rmj.previous_run_error_log as previousRunErrorLog, 
rmj.previous_run_error_message as previousRunErrorMessage, "
+                    + "rmj.number_of_runs as numberOfRuns, rmj.is_active as 
isActive, rmj.run_as_userid as runAsUserId, "
+                    + "sr.id as reportId, sr.report_name as reportName, 
sr.report_type as reportType, sr.report_subtype as reportSubType, "
+                    + "sr.report_category as reportCategory, sr.report_sql as 
reportSql, sr.description as reportDescription, "
+                    + "sr.core_report as coreReport, sr.use_report as 
useReport "
+                    + "from m_report_mailing_job rmj "
+                    + "inner join m_appuser cbu "
+                    + "on cbu.id = rmj.createdby_id "
+                    + "left join m_appuser mbu "
+                    + "on mbu.id = rmj.lastmodifiedby_id "
+                    + "left join stretchy_report sr "
+                    + "on rmj.stretchy_report_id = sr.id";
+        }
+
+        @Override
+        public ReportMailingJobData mapRow(ResultSet rs, int rowNum) throws 
SQLException {
+            final Long id = rs.getLong("id");
+            final String name = rs.getString("name");
+            final String description = rs.getString("description");
+            final DateTime startDateTime = JdbcSupport.getDateTime(rs, 
"startDateTime");
+            final String recurrence = rs.getString("recurrence");
+            final LocalDate createdOnDate = JdbcSupport.getLocalDate(rs, 
"createdOnDate");
+            final LocalDate updatedOnDate = JdbcSupport.getLocalDate(rs, 
"updatedOnDate");
+            final String emailRecipients = rs.getString("emailRecipients");
+            final String emailSubject = rs.getString("emailSubject");
+            final String emailMessage = rs.getString("emailMessage");
+            final String emailAttachmentFileFormatString = 
rs.getString("emailAttachmentFileFormat");
+            EnumOptionData emailAttachmentFileFormat = null;
+            
+            if (emailAttachmentFileFormatString != null) {
+                ReportMailingJobEmailAttachmentFileFormat format = 
ReportMailingJobEmailAttachmentFileFormat.newInstance(emailAttachmentFileFormatString);
+                
+                emailAttachmentFileFormat = format.toEnumOptionData();
+            }
+            
+            final String stretchyReportParamMap = 
rs.getString("stretchyReportParamMap");
+            final DateTime previousRunDateTime = JdbcSupport.getDateTime(rs, 
"previousRunDateTime");
+            final DateTime nextRunDateTime = JdbcSupport.getDateTime(rs, 
"nextRunDateTime");
+            final String previousRunStatus = rs.getString("previousRunStatus");
+            final String previousRunErrorLog = 
rs.getString("previousRunErrorLog");
+            final String previousRunErrorMessage = 
rs.getString("previousRunErrorMessage");
+            final Integer numberOfRuns = JdbcSupport.getInteger(rs, 
"numberOfRuns");
+            final boolean isActive = rs.getBoolean("isActive");
+            final String createdByUsername = rs.getString("createdByUsername");
+            final String createdByFirstname = 
rs.getString("createdByFirstname");
+            final String createdByLastname = rs.getString("createdByLastname");
+            final String updatedByUsername = rs.getString("updatedByUsername");
+            final String updatedByFirstname = 
rs.getString("updatedByFirstname");
+            final String updatedByLastname = rs.getString("updatedByLastname");
+            final ReportMailingJobTimelineData timeline = new 
ReportMailingJobTimelineData(createdOnDate, createdByUsername, 
+                    createdByFirstname, createdByLastname, updatedOnDate, 
updatedByUsername, updatedByFirstname, updatedByLastname);
+            final Long runAsUserId = JdbcSupport.getLong(rs, "runAsUserId");
+            
+            final Long reportId = JdbcSupport.getLong(rs, "reportId");
+            final String reportName = rs.getString("reportName");
+            final String reportType = rs.getString("reportType");
+            final String reportSubType = rs.getString("reportSubType");
+            final String reportCategory = rs.getString("reportCategory");
+            final String reportSql = rs.getString("reportSql");
+            final String reportDescription = rs.getString("reportDescription");
+            final boolean coreReport = rs.getBoolean("coreReport");
+            final boolean useReport = rs.getBoolean("useReport");
+            
+            final ReportData stretchyReport = new ReportData(reportId, 
reportName, reportType, reportSubType, reportCategory, 
+                    reportDescription, reportSql, coreReport, useReport, null);
+            
+            return ReportMailingJobData.newInstance(id, name, description, 
startDateTime, recurrence, timeline, emailRecipients, 
+                    emailSubject, emailMessage, emailAttachmentFileFormat, 
stretchyReport, stretchyReportParamMap, previousRunDateTime, 
+                    nextRunDateTime, previousRunStatus, previousRunErrorLog, 
previousRunErrorMessage, numberOfRuns, isActive, 
+                    runAsUserId);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java
new file mode 100644
index 0000000..9f4a204
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformService.java
@@ -0,0 +1,34 @@
+/**
+ * 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.reportmailingjob.service;
+
+import org.apache.fineract.infrastructure.core.service.Page;
+import org.apache.fineract.infrastructure.core.service.SearchParameters;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobRunHistoryData;
+
+public interface ReportMailingJobRunHistoryReadPlatformService {
+    /** 
+     * Retrieve all report mailing run history with similar job id to the one 
passed
+     * 
+     * @param reportMailingJobId ReportMailingJob identifier
+     * @param searchParameters {@link SearchParameters} object
+     * @return collection of {@link ReportMailingJobRunHistoryData} objects
+     **/
+    Page<ReportMailingJobRunHistoryData> retrieveRunHistoryByJobId(Long 
reportMailingJobId, SearchParameters searchParameters);
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java
new file mode 100644
index 0000000..4aeb68f
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobRunHistoryReadPlatformServiceImpl.java
@@ -0,0 +1,106 @@
+/**
+ * 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.reportmailingjob.service;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+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.core.service.SearchParameters;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobRunHistoryData;
+import org.joda.time.DateTime;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ReportMailingJobRunHistoryReadPlatformServiceImpl implements 
ReportMailingJobRunHistoryReadPlatformService {
+    private final JdbcTemplate jdbcTemplate;
+    private final ReportMailingJobRunHistoryMapper 
reportMailingJobRunHistoryMapper;
+    private final PaginationHelper<ReportMailingJobRunHistoryData> 
paginationHelper = new PaginationHelper<>();
+    
+    @Autowired
+    public ReportMailingJobRunHistoryReadPlatformServiceImpl(final 
RoutingDataSource dataSource) {
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+        this.reportMailingJobRunHistoryMapper = new 
ReportMailingJobRunHistoryMapper();
+    }
+    
+    @Override
+    public Page<ReportMailingJobRunHistoryData> 
retrieveRunHistoryByJobId(final Long reportMailingJobId, 
+            final SearchParameters searchParameters) {
+        final StringBuilder sqlStringBuilder = new StringBuilder(200);
+        final List<Object> queryParameters = new ArrayList<>();
+        
+        sqlStringBuilder.append("select SQL_CALC_FOUND_ROWS ");
+        
sqlStringBuilder.append(this.reportMailingJobRunHistoryMapper.ReportMailingJobRunHistorySchema());
+        
+        if (reportMailingJobId != null) {
+            sqlStringBuilder.append(" where rmjrh.job_id = ? ");
+            queryParameters.add(reportMailingJobId);
+        }
+        
+        if (searchParameters.isOrderByRequested()) {
+            sqlStringBuilder.append(" order by 
").append(searchParameters.getOrderBy());
+
+            if (searchParameters.isSortOrderProvided()) {
+                sqlStringBuilder.append(" 
").append(searchParameters.getSortOrder());
+            }
+        }
+
+        if (searchParameters.isLimited()) {
+            sqlStringBuilder.append(" limit 
").append(searchParameters.getLimit());
+            
+            if (searchParameters.isOffset()) {
+                sqlStringBuilder.append(" offset 
").append(searchParameters.getOffset());
+            }
+        }
+        
+        return this.paginationHelper.fetchPage(this.jdbcTemplate, "SELECT 
FOUND_ROWS()", sqlStringBuilder.toString(), 
+                queryParameters.toArray(), 
this.reportMailingJobRunHistoryMapper);
+    }
+    
+    private static final class ReportMailingJobRunHistoryMapper implements 
RowMapper<ReportMailingJobRunHistoryData> {
+        public String ReportMailingJobRunHistorySchema() {
+            return "rmjrh.id, rmjrh.job_id as reportMailingJobId, 
rmjrh.start_datetime as startDateTime, "
+                    + "rmjrh.end_datetime as endDateTime, rmjrh.status, 
rmjrh.error_message as errorMessage, "
+                    + "rmjrh.error_log as errorLog "
+                    + "from m_report_mailing_job_run_history rmjrh";
+        }
+        
+        @Override
+        public ReportMailingJobRunHistoryData mapRow(ResultSet rs, int rowNum) 
throws SQLException {
+            final Long id = JdbcSupport.getLong(rs, "id");
+            final Long reportMailingJobId = JdbcSupport.getLong(rs, 
"reportMailingJobId");
+            final DateTime startDateTime = JdbcSupport.getDateTime(rs, 
"startDateTime");
+            final DateTime endDateTime = JdbcSupport.getDateTime(rs, 
"endDateTime");
+            final String status = rs.getString("status");
+            final String errorMessage = rs.getString("errorMessage");
+            final String errorLog = rs.getString("errorLog");
+            
+            return ReportMailingJobRunHistoryData.newInstance(id, 
reportMailingJobId, startDateTime, endDateTime, status, 
+                    errorMessage, errorLog);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.java
new file mode 100644
index 0000000..ce54919
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformService.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.reportmailingjob.service;
+
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
+
+public interface ReportMailingJobWritePlatformService {
+    CommandProcessingResult createReportMailingJob(JsonCommand jsonCommand);
+    CommandProcessingResult updateReportMailingJob(Long reportMailingJobId, 
JsonCommand jsonCommand);
+    CommandProcessingResult deleteReportMailingJob(Long reportMailingJobId);
+    void executeReportMailingJobs() throws JobExecutionException;
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
new file mode 100644
index 0000000..02972a9
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/service/ReportMailingJobWritePlatformServiceImpl.java
@@ -0,0 +1,495 @@
+/**
+ * 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.reportmailingjob.service;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+
+import org.apache.commons.lang.StringUtils;
+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.core.service.DateUtils;
+import org.apache.fineract.infrastructure.dataqueries.domain.Report;
+import 
org.apache.fineract.infrastructure.dataqueries.domain.ReportRepositoryWrapper;
+import 
org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
+import 
org.apache.fineract.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository;
+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.report.provider.ReportingProcessServiceProvider;
+import 
org.apache.fineract.infrastructure.report.service.ReportingProcessService;
+import 
org.apache.fineract.infrastructure.reportmailingjob.ReportMailingJobConstants;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailData;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobPreviousRunStatus;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption;
+import 
org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJob;
+import 
org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepository;
+import 
org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRepositoryWrapper;
+import 
org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistory;
+import 
org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobRunHistoryRepository;
+import 
org.apache.fineract.infrastructure.reportmailingjob.util.ReportMailingJobDateUtil;
+import 
org.apache.fineract.infrastructure.reportmailingjob.validation.ReportMailingJobValidator;
+import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+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 com.sun.jersey.core.util.MultivaluedMapImpl;
+
+@Service
+public class ReportMailingJobWritePlatformServiceImpl implements 
ReportMailingJobWritePlatformService {
+    
+    private final static Logger logger = 
LoggerFactory.getLogger(ReportMailingJobWritePlatformServiceImpl.class);
+    private final ReportRepositoryWrapper reportRepositoryWrapper;
+    private final ReportMailingJobValidator reportMailingJobValidator;
+    private final ReportMailingJobRepositoryWrapper 
reportMailingJobRepositoryWrapper;
+    private final ReportMailingJobRepository reportMailingJobRepository;
+    private final PlatformSecurityContext platformSecurityContext;
+    private final ReportMailingJobEmailService reportMailingJobEmailService;
+    private final ReadReportingService readReportingService;
+    private final ReportingProcessServiceProvider 
reportingProcessServiceProvider;
+    private final ReportMailingJobRunHistoryRepository 
reportMailingJobRunHistoryRepository;
+    private final static String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    
+    @Autowired
+    public ReportMailingJobWritePlatformServiceImpl(final 
ReportRepositoryWrapper reportRepositoryWrapper, 
+            final ReportMailingJobValidator reportMailingJobValidator, 
+            final ReportMailingJobRepositoryWrapper 
reportMailingJobRepositoryWrapper, 
+            final ReportMailingJobRepository reportMailingJobRepository, 
+            final PlatformSecurityContext platformSecurityContext, 
+            final ReportMailingJobEmailService reportMailingJobEmailService,  
+            final ReadReportingService readReportingService, 
+            final ReportMailingJobRunHistoryRepository 
reportMailingJobRunHistoryRepository, 
+            final ReportingProcessServiceProvider 
reportingProcessServiceProvider) {
+        this.reportRepositoryWrapper = reportRepositoryWrapper;
+        this.reportMailingJobValidator = reportMailingJobValidator;
+        this.reportMailingJobRepositoryWrapper = 
reportMailingJobRepositoryWrapper;
+        this.reportMailingJobRepository = 
reportMailingJobRepositoryWrapper.getReportMailingJobRepository();
+        this.platformSecurityContext = platformSecurityContext;
+        this.reportMailingJobEmailService = reportMailingJobEmailService;
+        this.readReportingService = readReportingService;
+        this.reportMailingJobRunHistoryRepository = 
reportMailingJobRunHistoryRepository;
+        this.reportingProcessServiceProvider = reportingProcessServiceProvider;
+    }
+
+    @Override
+    @Transactional
+    public CommandProcessingResult createReportMailingJob(JsonCommand 
jsonCommand) {
+        try {
+            // validate the create request
+            this.reportMailingJobValidator.validateCreateRequest(jsonCommand);
+            
+            final AppUser appUser = 
this.platformSecurityContext.authenticatedUser();
+            
+            // get the stretchy Report object
+            final Report stretchyReport = 
this.reportRepositoryWrapper.findOneThrowExceptionIfNotFound(jsonCommand.longValueOfParameterNamed(
+                    ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME));
+            
+            // create an instance of ReportMailingJob class from the 
JsonCommand object
+            final ReportMailingJob reportMailingJob = 
ReportMailingJob.newInstance(jsonCommand, stretchyReport, appUser);
+            
+            // save entity
+            this.reportMailingJobRepository.save(reportMailingJob);
+            
+            return new 
CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).
+                    withEntityId(reportMailingJob.getId()).build();
+        } catch (final DataIntegrityViolationException dve) {
+            handleDataIntegrityIssues(jsonCommand, dve);
+            
+            return CommandProcessingResult.empty();
+        }
+    }
+
+    @Override
+    @Transactional
+    public CommandProcessingResult updateReportMailingJob(Long 
reportMailingJobId, JsonCommand jsonCommand) {
+        try {
+            // validate the update request
+            this.reportMailingJobValidator.validateUpdateRequest(jsonCommand);
+            
+            // retrieve the ReportMailingJob object from the database
+            final ReportMailingJob reportMailingJob = 
this.reportMailingJobRepositoryWrapper.findOneThrowExceptionIfNotFound(reportMailingJobId);
+            
+            final Map<String, Object> changes = 
reportMailingJob.update(jsonCommand);
+            
+            // get the recurrence rule string
+            final String recurrence = reportMailingJob.getRecurrence();
+            
+            // get the next run DateTime from the ReportMailingJob entity
+            DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime();
+            
+            // check if the stretchy report id was updated
+            if 
(changes.containsKey(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME)) {
+                final Long stretchyReportId = (Long) 
changes.get(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME);
+                final Report stretchyReport = 
this.reportRepositoryWrapper.findOneThrowExceptionIfNotFound(stretchyReportId);
+                
+                // update the stretchy report
+                reportMailingJob.update(stretchyReport);
+            }
+            
+            // check if the recurrence was updated
+            if 
(changes.containsKey(ReportMailingJobConstants.RECURRENCE_PARAM_NAME)) {
+                
+                // go ahead if the recurrence is not null
+                if (StringUtils.isNotBlank(recurrence)) {
+                    // set the start DateTime to the current tenant date time
+                    DateTime startDateTime = 
DateUtils.getLocalDateTimeOfTenant().toDateTime();
+                    
+                    // check if the start DateTime was updated
+                    if 
(changes.containsKey(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME)) {
+                        // get the updated start DateTime
+                        startDateTime = reportMailingJob.getStartDateTime();
+                    }
+                    
+                    startDateTime = reportMailingJob.getStartDateTime();
+                    
+                    // get the next recurring DateTime
+                    final DateTime nextRecurringDateTime = 
this.createNextRecurringDateTime(recurrence, startDateTime);
+                    
+                    // update the next run time property
+                    
reportMailingJob.updateNextRunDateTime(nextRecurringDateTime);
+                    
+                 // check if the next run DateTime is not empty and the 
recurrence is empty
+                } else if (StringUtils.isBlank(recurrence) && (nextRunDateTime 
!= null)) {
+                    // the next run DateTime should be set to null
+                    reportMailingJob.updateNextRunDateTime(null);
+                }
+            }
+            
+            if 
(changes.containsKey(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME)) {
+                final DateTime startDateTime = 
reportMailingJob.getStartDateTime();
+                
+                // initially set the next recurring date time to the new start 
date time
+                DateTime nextRecurringDateTime = startDateTime;
+                
+                // ensure that the recurrence pattern string is not empty
+                if (StringUtils.isNotBlank(recurrence)) {
+                    // get the next recurring DateTime
+                    nextRecurringDateTime = 
this.createNextRecurringDateTime(recurrence, startDateTime);
+                }
+                
+                // update the next run time property
+                reportMailingJob.updateNextRunDateTime(nextRecurringDateTime);
+            }
+            
+            if (!changes.isEmpty()) {
+                // save and flush immediately so any data integrity exception 
can be handled in the "catch" block
+                this.reportMailingJobRepository.saveAndFlush(reportMailingJob);
+            }
+            
+            return new CommandProcessingResultBuilder().
+                    withCommandId(jsonCommand.commandId()).
+                    withEntityId(reportMailingJob.getId()).
+                    with(changes).
+                    build();
+        } catch (final DataIntegrityViolationException dve) {
+            handleDataIntegrityIssues(jsonCommand, dve);
+            
+            return CommandProcessingResult.empty();
+        }
+    }
+
+    @Override
+    @Transactional
+    public CommandProcessingResult deleteReportMailingJob(Long 
reportMailingJobId) {
+        // retrieve the ReportMailingJob object from the database
+        final ReportMailingJob reportMailingJob = 
this.reportMailingJobRepositoryWrapper.findOneThrowExceptionIfNotFound(reportMailingJobId);
+        
+        // delete the report mailing job by setting the isDeleted property to 
1 and altering the name
+        reportMailingJob.delete();
+        
+        // save the report mailing job entity
+        this.reportMailingJobRepository.save(reportMailingJob);
+        
+        return new 
CommandProcessingResultBuilder().withEntityId(reportMailingJobId).build();
+    }
+    
+    @Override
+    @CronTarget(jobName = JobName.EXECUTE_REPORT_MAILING_JOBS)
+    public void executeReportMailingJobs() throws JobExecutionException {
+        final Collection<ReportMailingJob> reportMailingJobCollection = 
this.reportMailingJobRepository.findByIsActiveTrueAndIsDeletedFalse();
+        
+        for (ReportMailingJob reportMailingJob : reportMailingJobCollection) {
+            // get the tenant's date as a DateTime object
+            final DateTime localDateTimeOftenant = 
DateUtils.getLocalDateTimeOfTenant().toDateTime();
+            final DateTime nextRunDateTime = 
reportMailingJob.getNextRunDateTime();
+            
+            if (nextRunDateTime != null && 
nextRunDateTime.isBefore(localDateTimeOftenant)) {
+                // get the emailAttachmentFileFormat enum object
+                final ReportMailingJobEmailAttachmentFileFormat 
emailAttachmentFileFormat = ReportMailingJobEmailAttachmentFileFormat.
+                        
newInstance(reportMailingJob.getEmailAttachmentFileFormat());
+                
+                if (emailAttachmentFileFormat != null && 
emailAttachmentFileFormat.isValid()) {
+                    final Report stretchyReport = 
reportMailingJob.getStretchyReport();
+                    final String reportName = (stretchyReport != null) ? 
stretchyReport.getReportName() : null;
+                    final StringBuilder errorLog = new StringBuilder();
+                    final Map<String, String> validateStretchyReportParamMap = 
this.reportMailingJobValidator.
+                            
validateStretchyReportParamMap(reportMailingJob.getStretchyReportParamMap());
+                    MultivaluedMap<String, String> reportParams = new 
MultivaluedMapImpl();
+                    
+                    if (validateStretchyReportParamMap != null) {
+                        Iterator<Map.Entry<String, String>> 
validateStretchyReportParamMapEntries = 
validateStretchyReportParamMap.entrySet().iterator();
+                        
+                        while 
(validateStretchyReportParamMapEntries.hasNext()) {
+                            Map.Entry<String, String> 
validateStretchyReportParamMapEntry = 
validateStretchyReportParamMapEntries.next();
+                            String key = 
validateStretchyReportParamMapEntry.getKey();
+                            String value = 
validateStretchyReportParamMapEntry.getValue();
+                            
+                            if (StringUtils.containsIgnoreCase(key, "date")) {
+                                ReportMailingJobStretchyReportParamDateOption 
reportMailingJobStretchyReportParamDateOption = 
+                                        
ReportMailingJobStretchyReportParamDateOption.newInstance(value);
+                                
+                                if 
(reportMailingJobStretchyReportParamDateOption.isValid()) {
+                                    value = 
ReportMailingJobDateUtil.getDateAsString(reportMailingJobStretchyReportParamDateOption);
+                                }
+                            }
+                            
+                            reportParams.add(key, value);
+                        }
+                    }
+                    
+                    // generate the report output stream, method in turn call 
another that sends the file to the email recipients
+                    this.generateReportOutputStream(reportMailingJob, 
emailAttachmentFileFormat, reportParams, reportName, errorLog);
+                    
+                    // update the previous run time, next run time, status, 
error log properties
+                    
this.updateReportMailingJobAfterJobExecution(reportMailingJob, errorLog, 
localDateTimeOftenant);
+                }
+            }
+        }
+    }
+    
+    /** 
+     * update the report mailing job entity after job execution 
+     * 
+     * @param reportMailingJob -- the report mailing job entity
+     * @param errorLog -- StringBuilder object containing the error log if any
+     * @param jobStartDateTime -- the start DateTime of the job
+     * @return None
+     **/
+    private void updateReportMailingJobAfterJobExecution(final 
ReportMailingJob reportMailingJob, final StringBuilder errorLog, 
+            final DateTime jobStartDateTime) {
+        final String recurrence = reportMailingJob.getRecurrence();
+        final DateTime nextRunDateTime = reportMailingJob.getNextRunDateTime();
+        ReportMailingJobPreviousRunStatus reportMailingJobPreviousRunStatus = 
ReportMailingJobPreviousRunStatus.SUCCESS;
+        
+        reportMailingJob.updatePreviousRunErrorLog(null);
+        
+        if (errorLog != null && errorLog.length() > 0) {
+            reportMailingJobPreviousRunStatus = 
ReportMailingJobPreviousRunStatus.ERROR;
+            reportMailingJob.updatePreviousRunErrorLog(errorLog.toString());
+        }
+        
+        reportMailingJob.increaseNumberOfRunsByOne();
+        
reportMailingJob.updatePreviousRunStatus(reportMailingJobPreviousRunStatus.getValue());
+        
reportMailingJob.updatePreviousRunDateTime(reportMailingJob.getNextRunDateTime());
+        
+        // check if the job has a recurrence pattern, if not deactivate the 
job. The job will only run once
+        if (StringUtils.isEmpty(recurrence)) {
+            // deactivate job
+            reportMailingJob.deactivate();
+            
+            // job will only run once, no next run time
+            reportMailingJob.updateNextRunDateTime(null);
+        } else if (nextRunDateTime != null) {
+            final DateTime nextRecurringDateTime = 
this.createNextRecurringDateTime(recurrence, nextRunDateTime);
+            
+            // finally update the next run date time property
+            reportMailingJob.updateNextRunDateTime(nextRecurringDateTime);
+        }
+        
+        // save the ReportMailingJob entity
+        this.reportMailingJobRepository.save(reportMailingJob);
+        
+        // create a new report mailing job run history entity
+        
this.createReportMailingJobRunHistroryAfterJobExecution(reportMailingJob, 
errorLog, jobStartDateTime, 
+                reportMailingJobPreviousRunStatus.getValue());
+    }
+    
+    /**
+     * create the next recurring DateTime from recurrence pattern, start 
DateTime and current DateTime
+     * 
+     * @param recurrencePattern
+     * @param startDateTime
+     * @return DateTime object
+     */
+    private DateTime createNextRecurringDateTime(final String 
recurrencePattern, final DateTime startDateTime) {
+        DateTime nextRecurringDateTime = null;
+        
+        // the recurrence pattern/rule cannot be empty
+        if (StringUtils.isNotBlank(recurrencePattern) && startDateTime != 
null) {
+            final LocalDate nextDayLocalDate = 
startDateTime.plus(1).toLocalDate();
+            final LocalDate nextRecurringLocalDate = 
CalendarUtils.getNextRecurringDate(recurrencePattern, 
startDateTime.toLocalDate(), 
+                    nextDayLocalDate);
+            final String nextDateTimeString = nextRecurringLocalDate + " " + 
startDateTime.getHourOfDay() + ":" + startDateTime.getMinuteOfHour() 
+                    + ":" + startDateTime.getSecondOfMinute();
+            final DateTimeFormatter dateTimeFormatter = 
DateTimeFormat.forPattern(DATETIME_FORMAT);
+            
+            nextRecurringDateTime = DateTime.parse(nextDateTimeString, 
dateTimeFormatter);
+        }
+        
+        return nextRecurringDateTime;
+    }
+    
+    /** 
+     * create a new report mailing job run history entity after job execution
+     * 
+     * @param reportMailingJob -- the report mailing job entity
+     * @param errorLog -- StringBuilder object containing the error log if any
+     * @param jobStartDateTime -- the start DateTime of the job
+     * @param jobRunStatus -- the status of the job (success/error)
+     * @return None
+     **/
+    private void createReportMailingJobRunHistroryAfterJobExecution(final 
ReportMailingJob reportMailingJob, final StringBuilder errorLog, 
+            final DateTime jobStartDateTime, final String jobRunStatus) {
+        final DateTime jobEndDateTime = 
DateUtils.getLocalDateTimeOfTenant().toDateTime();
+        final String errorLogToString = (errorLog != null) ? 
errorLog.toString() : null;
+        final ReportMailingJobRunHistory reportMailingJobRunHistory = 
ReportMailingJobRunHistory.newInstance(reportMailingJob, jobStartDateTime, 
+                jobEndDateTime, jobRunStatus, null, errorLogToString);
+        
+        
this.reportMailingJobRunHistoryRepository.save(reportMailingJobRunHistory);
+    }
+
+    /** 
+     * Handle any SQL data integrity issue 
+     *
+     * @param jsonCommand -- JsonCommand object
+     * @param dve -- data integrity exception object
+     * @return None
+     **/
+    private void handleDataIntegrityIssues(final JsonCommand jsonCommand, 
final DataIntegrityViolationException dve) {
+        final Throwable realCause = dve.getMostSpecificCause();
+        
+        if 
(realCause.getMessage().contains(ReportMailingJobConstants.NAME_PARAM_NAME)) {
+            final String name = 
jsonCommand.stringValueOfParameterNamed(ReportMailingJobConstants.NAME_PARAM_NAME);
+            throw new 
PlatformDataIntegrityException("error.msg.report.mailing.job.duplicate.name", 
"Report mailing job with name `" + name + "` already exists",
+                    ReportMailingJobConstants.NAME_PARAM_NAME, name);
+        }
+
+        logger.error(dve.getMessage(), dve);
+        
+        throw new 
PlatformDataIntegrityException("error.msg.charge.unknown.data.integrity.issue",
+                "Unknown data integrity issue with resource: " + 
realCause.getMessage());
+    }
+    
+    /** 
+     * generate the report output stream
+     * 
+     * @param reportMailingJob
+     * @param emailAttachmentFileFormat
+     * @param reportParams
+     * @param reportName
+     * @param errorLog
+     * @return the error log StringBuilder object
+     */
+    private StringBuilder generateReportOutputStream(final ReportMailingJob 
reportMailingJob, final ReportMailingJobEmailAttachmentFileFormat 
emailAttachmentFileFormat, 
+            final MultivaluedMap<String, String> reportParams, final String 
reportName, final StringBuilder errorLog) {
+        
+        try {
+            final String reportType = 
this.readReportingService.getReportType(reportName);
+            final ReportingProcessService reportingProcessService = 
this.reportingProcessServiceProvider.findReportingProcessService(reportType);
+            
+            if (reportingProcessService != null) {
+                final Response processReport = 
reportingProcessService.processRequest(reportName, reportParams);
+                final Object reponseObject = (processReport != null) ? 
processReport.getEntity() : null;
+                
+                if (reponseObject != null && 
reponseObject.getClass().equals(ByteArrayOutputStream.class)) {
+                    final ByteArrayOutputStream byteArrayOutputStream = 
ByteArrayOutputStream.class.cast(reponseObject);
+                    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 == null) || 
byteArrayOutputStream.size() == 0) {
+                        errorLog.append("Report processing failed, empty 
output stream created");
+                    } else if ((errorLog != null && errorLog.length() == 0) && 
(byteArrayOutputStream.size() > 0)) {
+                        final String fileName = fileNameWithoutExtension + "." 
+ emailAttachmentFileFormat.getValue();
+                        
+                        // send the file to email recipients
+                        this.sendReportFileToEmailRecipients(reportMailingJob, 
fileName, byteArrayOutputStream, errorLog);
+                    }
+                } else {
+                    errorLog.append("Response object entity is not equal to 
ByteArrayOutputStream ---------- ");
+                }
+            } else {
+                errorLog.append("ReportingProcessService object is null 
---------- ");
+            }
+        } catch (Exception e) {
+            errorLog.append("The 
ReportMailingJobWritePlatformServiceImpl.generateReportOutputStream method 
threw an Exception: "
+                    + e + " ---------- ");
+        }
+        
+        return errorLog;
+    }
+    
+    /** 
+     * send report file to email recipients
+     * 
+     * @param reportMailingJob
+     * @param fileName
+     * @param byteArrayOutputStream
+     * @param errorLog
+     */
+    private void sendReportFileToEmailRecipients(final ReportMailingJob 
reportMailingJob, final String fileName, 
+            final ByteArrayOutputStream byteArrayOutputStream, final 
StringBuilder errorLog) {
+        final Set<String> emailRecipients = 
this.reportMailingJobValidator.validateEmailRecipients(reportMailingJob.getEmailRecipients());
+        
+        try {
+            final File file = new File(fileName);
+            final FileOutputStream outputStream = new FileOutputStream(file);
+            byteArrayOutputStream.writeTo(outputStream);
+            
+            for (String emailRecipient : emailRecipients) {
+                final ReportMailingJobEmailData reportMailingJobEmailData = 
new ReportMailingJobEmailData(emailRecipient, 
+                        reportMailingJob.getEmailMessage(), 
reportMailingJob.getEmailSubject(), file);
+                
+                
this.reportMailingJobEmailService.sendEmailWithAttachment(reportMailingJobEmailData);
+            }
+            
+            outputStream.close();
+            
+        } catch (IOException e) {
+            errorLog.append("The 
ReportMailingJobWritePlatformServiceImpl.sendReportFileToEmailRecipients method 
threw an IOException "
+                    + "exception: " + e + " ---------- ");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java
new file mode 100644
index 0000000..a1d97fa
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/util/ReportMailingJobDateUtil.java
@@ -0,0 +1,114 @@
+/**
+ * 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.reportmailingjob.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobStretchyReportParamDateOption;
+
+public class ReportMailingJobDateUtil {
+    public static final String MYSQL_DATE_FORMAT = "yyyy-MM-dd";
+    
+    /** 
+     * get the current date as string using the mysql date format yyyy-MM-dd 
+     **/
+    public static String getTodayDateAsString() {
+        // get a calendar instance, which defaults to "now"
+        Calendar calendar = Calendar.getInstance();
+        
+        // get a date to represent "today"
+        Date today = calendar.getTime();
+        
+        // get a SimpleDateFormat instance, passing the mysql date format as 
parameter
+        SimpleDateFormat simpleDateFormat = new 
SimpleDateFormat(MYSQL_DATE_FORMAT);
+        
+        // return date as string
+        return simpleDateFormat.format(today);
+    }
+    
+    /** 
+     * get the yesterday's date as string using the mysql date format 
yyyy-MM-dd 
+     **/
+    public static String getYesterdayDateAsString() {
+        // get a calendar instance, which defaults to "now"
+        Calendar calendar = Calendar.getInstance();
+        
+        // add one day to the date/calendar
+        calendar.add(Calendar.DAY_OF_YEAR, -1);
+         
+        // now get "yesterday"
+        Date yesterday = calendar.getTime();
+        
+        // get a SimpleDateFormat instance, passing the mysql date format as 
parameter
+        SimpleDateFormat simpleDateFormat = new 
SimpleDateFormat(MYSQL_DATE_FORMAT);
+        
+        // return date as string
+        return simpleDateFormat.format(yesterday);
+    }
+    
+    /** 
+     * get the tomorrow's date as string using the mysql date format 
yyyy-MM-dd 
+     **/
+    public static String getTomorrowDateAsString() {
+        // get a calendar instance, which defaults to "now"
+        Calendar calendar = Calendar.getInstance();
+        
+        // add one day to the date/calendar
+        calendar.add(Calendar.DAY_OF_YEAR, 1);
+         
+        // now get "tomorrow"
+        Date tomorrow = calendar.getTime();
+        
+        // get a SimpleDateFormat instance, passing the mysql date format as 
parameter
+        SimpleDateFormat simpleDateFormat = new 
SimpleDateFormat(MYSQL_DATE_FORMAT);
+        
+        // return date as string
+        return simpleDateFormat.format(tomorrow);
+    }
+    
+    /** 
+     * get date as string based on the value of the {@link 
ReportMailingJobStretchyReportParamDateOption} object
+     * 
+     * @param reportMailingJobStretchyReportParamDateOption {@link 
ReportMailingJobStretchyReportParamDateOption} Enum
+     **/
+    public static String getDateAsString(final 
ReportMailingJobStretchyReportParamDateOption 
reportMailingJobStretchyReportParamDateOption) {
+        String dateAsString = null;
+        
+        switch (reportMailingJobStretchyReportParamDateOption) {
+            case TODAY:
+                dateAsString = getTodayDateAsString();
+                break;
+                
+            case YESTERDAY:
+                dateAsString = getYesterdayDateAsString();
+                break;
+                
+            case TOMORROW:
+                dateAsString = getTomorrowDateAsString();
+                break;
+                
+            default:
+                break;
+        }
+        
+        return dateAsString;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java
new file mode 100644
index 0000000..c3bbaf8
--- /dev/null
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/validation/ReportMailingJobValidator.java
@@ -0,0 +1,319 @@
+/**
+ * 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.reportmailingjob.validation;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
+import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
+import 
org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import 
org.apache.fineract.infrastructure.reportmailingjob.ReportMailingJobConstants;
+import 
org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailAttachmentFileFormat;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.type.TypeReference;
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+@Component
+public class ReportMailingJobValidator {
+    private final FromJsonHelper fromJsonHelper;
+    private static final String EMAIL_REGEX = 
"^[\\w!#$%&’*+/=?`{|}~^-]+(?:\\.[\\w!#$%&’*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$";
+    private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
+    
+    @Autowired
+    public ReportMailingJobValidator(final FromJsonHelper fromJsonHelper) {
+        this.fromJsonHelper = fromJsonHelper;
+    }
+    
+    /** 
+     * validate the request to create a new report mailing job 
+     * 
+     * @param jsonCommand -- the JSON command object (instance of the 
JsonCommand class)
+     * @return None
+     **/
+    public void validateCreateRequest(final JsonCommand jsonCommand) {
+        final String jsonString = jsonCommand.json();
+        final JsonElement jsonElement = jsonCommand.parsedJson();
+
+        if (StringUtils.isBlank(jsonString)) { 
+            throw new InvalidJsonException(); 
+        }
+        
+        final Type typeToken = new TypeToken<Map<String, Object>>() 
{}.getType();
+        this.fromJsonHelper.checkForUnsupportedParameters(typeToken, 
jsonString,
+                ReportMailingJobConstants.CREATE_REQUEST_PARAMETERS);
+        
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder dataValidatorBuilder = new 
DataValidatorBuilder(dataValidationErrors).
+                
resource(StringUtils.lowerCase(ReportMailingJobConstants.REPORT_MAILING_JOB_RESOURCE_NAME));
+        
+        final String name = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.NAME_PARAM_NAME,
 jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.NAME_PARAM_NAME).value(name).notBlank().notExceedingLengthOf(100);
+        
+        final String startDateTime = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME,
 
+                jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME).value(startDateTime).notBlank();
+        
+        final Integer stretchyReportId = 
this.fromJsonHelper.extractIntegerWithLocaleNamed(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME,
 
+                jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME).value(stretchyReportId).notNull().
+                integerGreaterThanZero();
+        
+        final String emailRecipients = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME,
 jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME).value(emailRecipients).notBlank();
+        
+        final String emailSubject = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME,
 jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME).value(emailSubject).notBlank().notExceedingLengthOf(100);
+        
+        final String emailMessage = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME,
 jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME).value(emailMessage).notBlank();
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME,
 jsonElement)) {
+            final Boolean isActive = 
this.fromJsonHelper.extractBooleanNamed(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME,
 
+                    jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME).value(isActive).notNull();
+        }
+        
+        final Integer emailAttachmentFileFormatId = 
this.fromJsonHelper.extractIntegerSansLocaleNamed(
+                
ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, 
jsonElement);
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).
+                value(emailAttachmentFileFormatId).notNull();
+
+        if (emailAttachmentFileFormatId != null) {
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).value(emailAttachmentFileFormatId).
+                    
isOneOfTheseValues(ReportMailingJobEmailAttachmentFileFormat.validIds());
+        }
+        
+        final String dateFormat = jsonCommand.dateFormat();
+        
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).notBlank();
+        
+        if (StringUtils.isNotEmpty(dateFormat)) {
+            
+            try {
+                final DateTimeFormatter dateTimeFormatter = 
DateTimeFormat.forPattern(dateFormat).withLocale(jsonCommand.extractLocale());
+                
+                // try to parse the date time string
+                LocalDateTime.parse(startDateTime, dateTimeFormatter);
+            }
+            
+            catch(IllegalArgumentException ex) {
+                
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).failWithCode("invalid.date.format");
+            }
+        }
+        
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+    }
+    
+    /** 
+     * validate the request to update a report mailing job 
+     * 
+     * @param jsonCommand -- the JSON command object (instance of the 
JsonCommand class)
+     * @return None
+     **/
+    public void validateUpdateRequest(final JsonCommand jsonCommand) {
+        final String jsonString = jsonCommand.json();
+        final JsonElement jsonElement = jsonCommand.parsedJson();
+
+        if (StringUtils.isBlank(jsonString)) { 
+            throw new InvalidJsonException(); 
+        }
+        
+        final Type typeToken = new TypeToken<Map<String, Object>>() 
{}.getType();
+        this.fromJsonHelper.checkForUnsupportedParameters(typeToken, 
jsonString,
+                ReportMailingJobConstants.UPDATE_REQUEST_PARAMETERS);
+        
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder dataValidatorBuilder = new 
DataValidatorBuilder(dataValidationErrors).
+                
resource(StringUtils.lowerCase(ReportMailingJobConstants.REPORT_MAILING_JOB_RESOURCE_NAME));
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.NAME_PARAM_NAME, 
jsonElement)) {
+            final String name = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.NAME_PARAM_NAME,
 jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.NAME_PARAM_NAME).value(name).notBlank().notExceedingLengthOf(100);
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME,
 jsonElement)) {
+            final Integer stretchyReportId = 
this.fromJsonHelper.extractIntegerWithLocaleNamed(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME,
 
+                jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.STRETCHY_REPORT_ID_PARAM_NAME).value(stretchyReportId).notNull().
+                integerGreaterThanZero();
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME,
 jsonElement)) {
+            final String emailRecipients = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME,
 jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_RECIPIENTS_PARAM_NAME).value(emailRecipients).notBlank();
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME,
 jsonElement)) {
+            final String emailSubject = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME,
 jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_SUBJECT_PARAM_NAME).value(emailSubject).notBlank().notExceedingLengthOf(100);
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME,
 jsonElement)) {
+            final String emailMessage = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME,
 jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_MESSAGE_PARAM_NAME).value(emailMessage).notBlank();
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME,
 jsonElement)) {
+            final Boolean isActive = 
this.fromJsonHelper.extractBooleanNamed(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME,
 
+                    jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.IS_ACTIVE_PARAM_NAME).value(isActive).notNull();
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME,
 jsonElement)) {
+            final Integer emailAttachmentFileFormatId = 
this.fromJsonHelper.extractIntegerSansLocaleNamed(
+                
ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, 
jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).
+                    value(emailAttachmentFileFormatId).notNull();
+            
+            if (emailAttachmentFileFormatId != null) {
+                
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).value(emailAttachmentFileFormatId).
+                        
isOneOfTheseValues(ReportMailingJobEmailAttachmentFileFormat.validIds());
+            }
+        }
+        
+        if 
(this.fromJsonHelper.parameterExists(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME,
 jsonElement)) {
+            final String dateFormat = jsonCommand.dateFormat();
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).notBlank();
+            
+            final String startDateTime = 
this.fromJsonHelper.extractStringNamed(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME,
 
+                jsonElement);
+            
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.START_DATE_TIME_PARAM_NAME).value(startDateTime).notBlank();
+            
+            if (StringUtils.isNotEmpty(dateFormat)) {
+                
+                try {
+                    final DateTimeFormatter dateTimeFormatter = 
DateTimeFormat.forPattern(dateFormat).withLocale(jsonCommand.extractLocale());
+                    
+                    // try to parse the date time string
+                    LocalDateTime.parse(startDateTime, dateTimeFormatter);
+                }
+                
+                catch(IllegalArgumentException ex) {
+                    
dataValidatorBuilder.reset().parameter(ReportMailingJobConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).failWithCode("invalid.date.format");
+                }
+            }
+        }
+        
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+    }
+    
+    /** 
+     * check if string is a valid email address 
+     * 
+     * @param email -- string to be validated
+     * @return true if string is a valid email address
+     **/
+    public boolean isValidEmail(String email) {
+        // this is the easiest check
+        if (email == null) {
+            return false;
+        }
+        
+        // this is another easy check
+        if (email.endsWith(".")) {
+            return false;
+        }
+        
+        // Check the whole email address structure
+        Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
+        
+        // check if the Matcher matches the email pattern
+        if (!emailMatcher.matches()) {
+            return false;
+        }
+        
+        return true;
+    }
+    
+    /** 
+     * Validate the email recipients string 
+     * 
+     * @param emailRecipients -- the email recipients string to be validated
+     * @return a hashset containing valid email addresses
+     **/
+    public Set<String> validateEmailRecipients(String emailRecipients) {
+        Set<String> emailRecipientsSet = new HashSet<>();
+        
+        if (emailRecipients != null) {
+            String[] split = emailRecipients.split(",");
+            
+            for (String emailAddress : split) {
+                emailAddress = emailAddress.trim();
+                
+                if (this.isValidEmail(emailAddress)) {
+                    emailRecipientsSet.add(emailAddress);
+                }
+            }
+        }
+        
+        return emailRecipientsSet;
+    }
+    
+    /** 
+     * validate the stretchy report param json string 
+     * 
+     * @param stretchyReportParamMap -- json string to be validated
+     * @return if string is valid or empty, a HashMap object, else null
+     **/
+    public HashMap<String,String> validateStretchyReportParamMap(String 
stretchyReportParamMap) {
+        HashMap<String,String> stretchyReportParamHashMap = new HashMap<>();
+        
+        if (!StringUtils.isEmpty(stretchyReportParamMap)) {
+            try {
+                stretchyReportParamHashMap = new 
ObjectMapper().readValue(stretchyReportParamMap, new 
TypeReference<HashMap<String,String>>(){});
+            }
+            
+            catch(Exception e) {
+                stretchyReportParamHashMap = null;
+            }
+        }
+        
+        return stretchyReportParamHashMap;
+    }
+    
+    /** 
+     * throw a PlatformApiDataValidationException exception if there are 
validation errors
+     * 
+     * @param dataValidationErrors -- list of ApiParameterError objects
+     * @return None
+     **/
+    private void throwExceptionIfValidationWarningsExist(final 
List<ApiParameterError> dataValidationErrors) {
+        if (!dataValidationErrors.isEmpty()) { 
+            throw new 
PlatformApiDataValidationException(dataValidationErrors); 
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/b30976bb/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql
----------------------------------------------------------------------
diff --git 
a/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql
 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql
new file mode 100644
index 0000000..155c77a
--- /dev/null
+++ 
b/fineract-provider/src/main/resources/sql/migrations/core_db/V312__report_mailing_job_module.sql
@@ -0,0 +1,61 @@
+create table if not exists m_report_mailing_job (
+id bigint primary key auto_increment,
+name varchar(100) not null,
+description text null,
+start_datetime datetime not null,
+recurrence varchar(100) null,
+created_date date not null,
+createdby_id bigint not null,
+lastmodified_date date null,
+lastmodifiedby_id bigint null,
+email_recipients text not null,
+email_subject varchar(100) not null,
+email_message text not null,
+email_attachment_file_format varchar(10) not null,
+stretchy_report_id int not null,
+stretchy_report_param_map text null,
+previous_run_datetime datetime null,
+next_run_datetime datetime null,
+previous_run_status varchar(10) null,
+previous_run_error_log text null,
+previous_run_error_message text null,
+number_of_runs int not null default 0,
+is_active tinyint(1) not null default 0,
+is_deleted tinyint(1) not null default 0,
+run_as_userid bigint not null,
+foreign key (createdby_id) references m_appuser(id),
+foreign key (lastmodifiedby_id) references m_appuser(id),
+foreign key (stretchy_report_id) references stretchy_report(id),
+foreign key (run_as_userid) references m_appuser(id),
+constraint unique_name unique (name)
+);
+
+create table if not exists m_report_mailing_job_run_history (
+id bigint primary key auto_increment,
+job_id bigint not null,
+start_datetime datetime not null,
+end_datetime datetime not null,
+status varchar(10) not null,
+error_message text null,
+error_log text null,
+foreign key (job_id) references m_report_mailing_job (id)
+);
+
+create table if not exists m_report_mailing_job_configuration (
+id int primary key auto_increment,
+name varchar(50) not null,
+`value` varchar(200) not null,
+constraint unique_name unique (name)
+);
+
+insert into m_permission (`grouping`, code, entity_name, action_name, 
can_maker_checker)
+values ('jobs', 'CREATE_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'CREATE', 0), 
+('jobs', 'UPDATE_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'UPDATE', 0), 
+('jobs', 'DELETE_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'DELETE', 0), 
+('jobs', 'READ_REPORTMAILINGJOB', 'REPORTMAILINGJOB', 'READ', 0);
+
+insert into m_report_mailing_job_configuration (name, `value`)
+values ('GMAIL_SMTP_SERVER', 'smtp.gmail.com'), ('GMAIL_SMTP_PORT', 587), 
('GMAIL_SMTP_USERNAME', ''), ('GMAIL_SMTP_PASSWORD', '');
+
+insert into job (name, display_name, cron_expression, create_time)
+values ('Execute Report Mailing Jobs', 'Execute Report Mailing Jobs', '0 0/15 
* * * ?', NOW());

Reply via email to