Email Campaigns PR

This commit is contained in:
Ippez Robert
2017-11-14 15:53:34 +03:00
committed by GitHub
parent d6accae348
commit c338c1758e
80 changed files with 6840 additions and 27 deletions
@@ -2929,7 +2929,7 @@ public class CommandWrapperBuilder {
this.href = "/smscampaigns/"+resourceId;
return this;
}
public CommandWrapperBuilder holdAmount(final Long accountId) {
this.actionName = "HOLDAMOUNT";
this.entityName = "SAVINGSACCOUNT";
@@ -3039,6 +3039,82 @@ public class CommandWrapperBuilder {
this.entityId = adHocId;
this.href = "/adhocquery/" + adHocId;
this.json = "{}";
public CommandWrapperBuilder createEmail() {
this.actionName = "CREATE";
this.entityName = "EMAIL";
this.entityId = null;
this.href = "/emailcampaigns/template";
return this;
}
public CommandWrapperBuilder updateEmail(final Long resourceId) {
this.actionName = "UPDATE";
this.entityName = "EMAIL";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId;
return this;
}
public CommandWrapperBuilder deleteEmail(final Long resourceId) {
this.actionName = "DELETE";
this.entityName = "EMAIL";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId;
return this;
}
public CommandWrapperBuilder createEmailCampaign() {
this.actionName = "CREATE";
this.entityName = "EMAIL_CAMPAIGN";
this.entityId = null;
this.href = "/emailcampaigns/campaign";
return this;
}
public CommandWrapperBuilder updateEmailCampaign(final Long resourceId) {
this.actionName = "UPDATE";
this.entityName = "EMAIL_CAMPAIGN";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId;
return this;
}
public CommandWrapperBuilder deleteEmailCampaign(final Long resourceId) {
this.actionName = "DELETE";
this.entityName = "EMAIL_CAMPAIGN";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId;
return this;
}
public CommandWrapperBuilder activateEmailCampaign(final Long resourceId) {
this.actionName = "ACTIVATE";
this.entityName = "EMAIL_CAMPAIGN";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId + "?command=activate";
return this;
}
public CommandWrapperBuilder closeEmailCampaign(final Long resourceId) {
this.actionName = "CLOSE";
this.entityName = "EMAIL_CAMPAIGN";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId + "?command=close";
return this;
}
public CommandWrapperBuilder reactivateEmailCampaign(final Long resourceId) {
this.actionName = "REACTIVATE";
this.entityName = "EMAIL_CAMPAIGN";
this.entityId = resourceId;
this.href = "/emailcampaigns/"+resourceId + "?command=reactivate";
return this;
}
public CommandWrapperBuilder updateEmailConfiguration() {
this.actionName = "UPDATE";
this.entityName = "EMAIL_CONFIGURATION";
this.href = "/emailcampaigns/configuration/";
return this;
}
}
@@ -0,0 +1,54 @@
/**
* 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;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class EmailApiConstants {
public static final String RESOURCE_NAME = "email";
// general
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
// request parameters
public static final String idParamName = "id";
public static final String groupIdParamName = "groupId";
public static final String clientIdParamName = "clientId";
public static final String staffIdParamName = "staffId";
public static final String subjectParamName = "emailSubject";
public static final String messageParamName = "emailMessage";
// response parameters
public static final String statusParamName = "status";
public static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, groupIdParamName, clientIdParamName, staffIdParamName, messageParamName));
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(messageParamName));
public static final String SMTP_SERVER = "SMTP_SERVER";
public static final String SMTP_PORT = "SMTP_PORT";
public static final String SMTP_USERNAME = "SMTP_USERNAME";
public static final String SMTP_PASSWORD = "SMTP_PASSWORD";
}
@@ -0,0 +1,82 @@
/**
* 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;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class ScheduledEmailConstants {
// define the API resource entity name
public static final String SCHEDULED_EMAIL_ENTITY_NAME = "SCHEDULEDEMAIL";
// general API resource request parameter constants
public static final String LOCALE_PARAM_NAME = "locale";
public static final String DATE_FORMAT_PARAM_NAME = "dateFormat";
// parameter constants for create/update entity API request
public static final String NAME_PARAM_NAME = "name";
public static final String DESCRIPTION_PARAM_NAME = "description";
public static final String START_DATE_TIME_PARAM_NAME = "startDateTime";
public static final String RECURRENCE_PARAM_NAME = "recurrence";
public static final String EMAIL_RECIPIENTS_PARAM_NAME = "emailRecipients";
public static final String EMAIL_SUBJECT_PARAM_NAME = "emailSubject";
public static final String EMAIL_MESSAGE_PARAM_NAME = "emailMessage";
public static final String EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME = "emailAttachmentFileFormatId";
public static final String STRETCHY_REPORT_ID_PARAM_NAME = "stretchyReportId";
public static final String STRETCHY_REPORT_PARAM_MAP_PARAM_NAME = "stretchyReportParamMap";
public static final String IS_ACTIVE_PARAM_NAME = "isActive";
// other parameter constants
public static final String ID_PARAM_NAME = "id";
public static final String EMAIL_ATTACHMENT_FILE_FORMAT_PARAM_NAME = "emailAttachmentFileFormat";
public static final String PREVIOUS_RUN_DATE_TIME_PARAM_NAME = "previousRunDateTime";
public static final String NEXT_RUN_DATE_TIME_PARAM_NAME = "nextRunDateTime";
public static final String PREVIOUS_RUN_STATUS = "previousRunStatus";
public static final String PREVIOUS_RUN_ERROR_LOG = "previousRunErrorLog";
public static final String PREVIOUS_RUN_ERROR_MESSAGE = "previousRunErrorMessage";
public static final String NUMBER_OF_RUNS = "numberOfRuns";
public static final String STRETCHY_REPORT_PARAM_NAME = "stretchyReport";
// list of permitted parameters for the create report mailing job request
public static final Set<String> CREATE_REQUEST_PARAMETERS = new HashSet<>(Arrays.asList(LOCALE_PARAM_NAME, DATE_FORMAT_PARAM_NAME,
NAME_PARAM_NAME, DESCRIPTION_PARAM_NAME, RECURRENCE_PARAM_NAME, EMAIL_RECIPIENTS_PARAM_NAME, EMAIL_SUBJECT_PARAM_NAME,
EMAIL_MESSAGE_PARAM_NAME, EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, STRETCHY_REPORT_ID_PARAM_NAME,
STRETCHY_REPORT_PARAM_MAP_PARAM_NAME, IS_ACTIVE_PARAM_NAME, START_DATE_TIME_PARAM_NAME));
// list of permitted parameters for the update report mailing job request
public static final Set<String> UPDATE_REQUEST_PARAMETERS = new HashSet<>(Arrays.asList(LOCALE_PARAM_NAME, DATE_FORMAT_PARAM_NAME,
NAME_PARAM_NAME, DESCRIPTION_PARAM_NAME, RECURRENCE_PARAM_NAME, EMAIL_RECIPIENTS_PARAM_NAME, EMAIL_SUBJECT_PARAM_NAME,
EMAIL_MESSAGE_PARAM_NAME, EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, STRETCHY_REPORT_ID_PARAM_NAME,
STRETCHY_REPORT_PARAM_MAP_PARAM_NAME, IS_ACTIVE_PARAM_NAME, START_DATE_TIME_PARAM_NAME));
// list of parameters that represent the properties of a report mailing job
public static final Set<String> REPORT_MAILING_JOB_DATA_PARAMETERS = new HashSet<>(Arrays.asList(ID_PARAM_NAME, NAME_PARAM_NAME,
DESCRIPTION_PARAM_NAME, RECURRENCE_PARAM_NAME, EMAIL_RECIPIENTS_PARAM_NAME, EMAIL_SUBJECT_PARAM_NAME, EMAIL_MESSAGE_PARAM_NAME,
EMAIL_ATTACHMENT_FILE_FORMAT_PARAM_NAME, STRETCHY_REPORT_PARAM_NAME, STRETCHY_REPORT_PARAM_MAP_PARAM_NAME, IS_ACTIVE_PARAM_NAME,
START_DATE_TIME_PARAM_NAME, PREVIOUS_RUN_DATE_TIME_PARAM_NAME, NEXT_RUN_DATE_TIME_PARAM_NAME, PREVIOUS_RUN_STATUS,
PREVIOUS_RUN_ERROR_LOG, PREVIOUS_RUN_ERROR_MESSAGE, NUMBER_OF_RUNS));
// report mailing job configuration names
public static final String GMAIL_SMTP_SERVER = "GMAIL_SMTP_SERVER";
public static final String GMAIL_SMTP_PORT = "GMAIL_SMTP_PORT";
public static final String GMAIL_SMTP_USERNAME = "GMAIL_SMTP_USERNAME";
public static final String GMAIL_SMTP_PASSWORD = "GMAIL_SMTP_PASSWORD";
}
@@ -0,0 +1,201 @@
/**
* 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.api;
import org.apache.fineract.accounting.journalentry.api.DateParam;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailData;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailConfigurationReadPlatformService;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Path("/email")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Component
@Scope("singleton")
public class EmailApiResource {
private final String resourceNameForPermissions = "Email";
private final PlatformSecurityContext context;
private final EmailReadPlatformService readPlatformService;
private final DefaultToApiJsonSerializer<EmailData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final EmailConfigurationReadPlatformService emailConfigurationReadPlatformService;
@Autowired
public EmailApiResource(final PlatformSecurityContext context, final EmailReadPlatformService readPlatformService,
final DefaultToApiJsonSerializer<EmailData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
EmailConfigurationReadPlatformService emailConfigurationReadPlatformService) {
this.context = context;
this.readPlatformService = readPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.emailConfigurationReadPlatformService = emailConfigurationReadPlatformService;
}
@GET
public String retrieveAllEmails(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final Collection<EmailData> emailMessages = this.readPlatformService.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, emailMessages);
}
@GET
@Path("pendingEmail")
public String retrievePendingEmail(@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.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(sqlSearch, offset, limit, orderBy, sortOrder);
Page<EmailData> emailMessages = this.readPlatformService.retrieveAllPending(searchParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, emailMessages);
}
@GET
@Path("sentEmail")
public String retrieveSentEmail(@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.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(sqlSearch, offset, limit, orderBy, sortOrder);
Page<EmailData> emailMessages = this.readPlatformService.retrieveAllSent(searchParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, emailMessages);
}
@GET
@Path("messageByStatus")
public String retrieveAllEmailByStatus(@QueryParam("sqlSearch") final String sqlSearch,
@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("status") final Long status,
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder,
@QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("toDate") final DateParam toDateParam,
@QueryParam("locale") final String locale, @QueryParam("dateFormat") final String dateFormat,@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
Date fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate("fromDate", dateFormat, locale);
}
Date toDate = null;
if (toDateParam != null) {
toDate = toDateParam.getDate("toDate", dateFormat, locale);
}
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(sqlSearch, offset, limit, status, fromDate,toDate, orderBy, sortOrder);
Page<EmailData> emailMessages = this.readPlatformService.retrieveEmailByStatus(searchParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, emailMessages);
}
@GET
@Path("failedEmail")
public String retrieveFailedEmail(@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.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final SearchParameters searchParameters = SearchParameters.forEmailCampaign(sqlSearch, offset, limit, orderBy, sortOrder);
Page<EmailData> emailMessages = this.readPlatformService.retrieveAllFailed(searchParameters);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, emailMessages);
}
@POST
public String create(final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createEmail().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@GET
@Path("{resourceId}")
public String retrieveOne(@PathParam("resourceId") final Long resourceId, @Context final UriInfo uriInfo) {
final EmailData emailMessage = this.readPlatformService.retrieveOne(resourceId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, emailMessage);
}
@PUT
@Path("{resourceId}")
public String update(@PathParam("resourceId") final Long resourceId, final String apiRequestBodyAsJson) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateEmail(resourceId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@DELETE
@Path("{resourceId}")
public String delete(@PathParam("resourceId") final Long resourceId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteEmail(resourceId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -0,0 +1,230 @@
/**
* 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.api;
import com.google.gson.JsonElement;
import org.apache.commons.lang.StringUtils;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.api.JsonQuery;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
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.EmailBusinessRulesData;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignData;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignReadPlatformService;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.util.Collection;
import java.util.HashSet;
/**
* Created with IntelliJ IDEA.
* User: andrew
* Date: 19-5-14
* Time: 15:17
* To change this template use File | Settings | File Templates.
*/
@Path("/email/campaign")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Component
@Scope("singleton")
public class EmailCampaignApiResource {
//change name to email campaign
private final String resourceNameForPermissions = "EMAIL_CAMPAIGN";
private final PlatformSecurityContext context;
private final DefaultToApiJsonSerializer<EmailBusinessRulesData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final EmailCampaignReadPlatformService emailCampaignReadPlatformService;
private final FromJsonHelper fromJsonHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<EmailCampaignData> emailCampaignDataDefaultToApiJsonSerializer;
private final EmailCampaignWritePlatformService emailCampaignWritePlatformService;
private final DefaultToApiJsonSerializer<PreviewCampaignMessage> previewCampaignMessageDefaultToApiJsonSerializer;
@Autowired
public EmailCampaignApiResource(final PlatformSecurityContext context,final DefaultToApiJsonSerializer<EmailBusinessRulesData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final EmailCampaignReadPlatformService emailCampaignReadPlatformService, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final DefaultToApiJsonSerializer<EmailCampaignData> emailCampaignDataDefaultToApiJsonSerializer,
final FromJsonHelper fromJsonHelper, final EmailCampaignWritePlatformService emailCampaignWritePlatformService,
final DefaultToApiJsonSerializer<PreviewCampaignMessage> previewCampaignMessageDefaultToApiJsonSerializer) {
this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.emailCampaignReadPlatformService = emailCampaignReadPlatformService;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.emailCampaignDataDefaultToApiJsonSerializer = emailCampaignDataDefaultToApiJsonSerializer;
this.fromJsonHelper = fromJsonHelper;
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
this.previewCampaignMessageDefaultToApiJsonSerializer = previewCampaignMessageDefaultToApiJsonSerializer;
}
@GET
@Path("{resourceId}")
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveOneCampaign(@PathParam("resourceId") final Long resourceId,@Context final UriInfo uriInfo){
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
EmailCampaignData emailCampaignData = this.emailCampaignReadPlatformService.retrieveOne(resourceId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.emailCampaignDataDefaultToApiJsonSerializer.serialize(settings,emailCampaignData);
}
@GET
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllCampaign(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final Collection<EmailCampaignData> emailCampaignDataCollection = this.emailCampaignReadPlatformService.retrieveAllCampaign();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.emailCampaignDataDefaultToApiJsonSerializer.serialize(settings,emailCampaignDataCollection);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createCampaign(final String apiRequestBodyAsJson,@Context final UriInfo uriInfo){
final CommandWrapper commandRequest = new CommandWrapperBuilder().createEmailCampaign().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@PUT
@Path("{resourceId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateCampaign(@PathParam("resourceId") final Long campaignId,final String apiRequestBodyAsJson,@Context final UriInfo uriInfo){
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateEmailCampaign(campaignId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
@POST
@Path("{resourceId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String activate(@PathParam("resourceId") final Long campaignId, @QueryParam("command") final String commandParam,
final String apiRequestBodyAsJson){
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
CommandProcessingResult result = null;
CommandWrapper commandRequest = null;
if (is(commandParam, "activate")) {
commandRequest = builder.activateEmailCampaign(campaignId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}else if (is(commandParam, "close")){
commandRequest = builder.closeEmailCampaign(campaignId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}else if (is(commandParam, "reactivate")){
commandRequest = builder.reactivateEmailCampaign(campaignId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
return this.toApiJsonSerializer.serialize(result);
}
@POST
@Path("preview")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String preview(final String apiRequestBodyAsJson,@Context final UriInfo uriInfo){
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
PreviewCampaignMessage campaignMessage = null;
final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
campaignMessage = this.emailCampaignWritePlatformService.previewMessage(query);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.previewCampaignMessageDefaultToApiJsonSerializer.serialize(settings,campaignMessage, new HashSet<String>());
}
@GET()
@Path("template")
public String template(@Context final UriInfo uriInfo){
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final Collection<EmailBusinessRulesData> emailBusinessRulesDataCollection = this.emailCampaignReadPlatformService.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings,emailBusinessRulesDataCollection);
}
@GET
@Path("template/{resourceId}")
public String retrieveOneTemplate(@PathParam("resourceId") final Long resourceId,@Context final UriInfo uriInfo){
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final EmailBusinessRulesData emailBusinessRulesData = this.emailCampaignReadPlatformService.retrieveOneTemplate(resourceId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings,emailBusinessRulesData);
}
@DELETE
@Path("{resourceId}")
public String delete(@PathParam("resourceId") final Long resourceId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteEmailCampaign(resourceId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
}
@@ -0,0 +1,94 @@
/**
* 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.api;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailData;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailConfigurationReadPlatformService;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailReadPlatformService;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.util.Collection;
@Path("/email/configuration")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Component
@Scope("singleton")
public class EmailConfigurationApiResource {
private final String resourceNameForPermissions = "EMAIL_CONFIGURATION";
private final PlatformSecurityContext context;
private final EmailReadPlatformService readPlatformService;
private final DefaultToApiJsonSerializer<EmailConfigurationData> toApiJsonSerializer;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final EmailConfigurationReadPlatformService emailConfigurationReadPlatformService;
@Autowired
public EmailConfigurationApiResource(final PlatformSecurityContext context, final EmailReadPlatformService readPlatformService,
final DefaultToApiJsonSerializer<EmailConfigurationData> toApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final EmailConfigurationReadPlatformService emailConfigurationReadPlatformService) {
this.context = context;
this.readPlatformService = readPlatformService;
this.toApiJsonSerializer = toApiJsonSerializer;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.emailConfigurationReadPlatformService = emailConfigurationReadPlatformService;
}
@GET
public String retrieveAll(@Context final UriInfo uriInfo){
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
final Collection<EmailConfigurationData> configuration = this.emailConfigurationReadPlatformService.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, configuration);
}
@PUT
public String updateConfiguration(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson){
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateEmailConfiguration().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -0,0 +1,96 @@
/**
* 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.data;
import java.util.Map;
public class EmailBusinessRulesData {
@SuppressWarnings("unused")
private final Long reportId;
@SuppressWarnings("unused")
private final String reportName;
@SuppressWarnings("unused")
private final String reportType;
@SuppressWarnings("unused")
private final String reportSubType;
@SuppressWarnings("unused")
private final String reportDescription;
@SuppressWarnings("unused")
private final Map<String,Object> reportParamName;
public EmailBusinessRulesData(final Long reportId, final String reportName,final String reportType, final Map<String,Object> reportParamName,
final String reportSubType, final String reportDescription) {
this.reportId = reportId;
this.reportName = reportName;
this.reportType = reportType;
this.reportParamName = reportParamName;
this.reportDescription = reportDescription;
this.reportSubType = reportSubType;
}
public static EmailBusinessRulesData instance(final Long reportId, final String reportName, final String reportType, final Map<String,Object> reportParamName,
final String reportSubType, final String reportDescription){
return new EmailBusinessRulesData(reportId,reportName,reportType,reportParamName,reportSubType,reportDescription);
}
public Map<String, Object> getReportParamName() {
return reportParamName;
}
public String getReportType() {
return reportType;
}
public String getReportName() {
return reportName;
}
public Long getReportId() {
return reportId;
}
public String getReportDescription() {
return reportDescription;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmailBusinessRulesData that = (EmailBusinessRulesData) o;
return reportId != null ? reportId.equals(that.reportId) : that.reportId == null;
}
@Override
public int hashCode() {
return reportId != null ? reportId.hashCode() : 0;
}
}
@@ -0,0 +1,144 @@
/**
* 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.data;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public class EmailCampaignData {
@SuppressWarnings("unused")
private Long id;
@SuppressWarnings("unused")
private final String campaignName;
@SuppressWarnings("unused")
private final Integer campaignType;
@SuppressWarnings("unused")
private final Long businessRuleId;
@SuppressWarnings("unused")
private final String paramValue;
@SuppressWarnings("unused")
private final EnumOptionData campaignStatus;
@SuppressWarnings("unused")
private final String emailSubject;
@SuppressWarnings("unused")
private final String emailMessage;
@SuppressWarnings("unused")
private final String emailAttachmentFileFormat;
@SuppressWarnings("unused")
private final Long stretchyReportId;
@SuppressWarnings("unused")
private final String stretchyReportParamMap;
@SuppressWarnings("unused")
private final DateTime nextTriggerDate;
@SuppressWarnings("unused")
private final LocalDate lastTriggerDate;
@SuppressWarnings("unused")
private final EmailCampaignTimeLine emailCampaignTimeLine;
@SuppressWarnings("unused")
private final DateTime recurrenceStartDate;
private final String recurrence;
private EmailCampaignData(final Long id, final String campaignName, final Integer campaignType, final Long businessRuleId,
final String paramValue, final EnumOptionData campaignStatus, final String emailSubject,
final String message, final String emailAttachmentFileFormat, final Long stretchyReportId,
final String stretchyReportParamMap, final DateTime nextTriggerDate, final LocalDate lastTriggerDate,
final EmailCampaignTimeLine emailCampaignTimeLine, final DateTime recurrenceStartDate, final String recurrence) {
this.id = id;
this.campaignName = campaignName;
this.campaignType = campaignType;
this.businessRuleId = businessRuleId;
this.paramValue = paramValue;
this.campaignStatus =campaignStatus;
this.emailSubject = emailSubject;
this.emailMessage = message;
this.emailAttachmentFileFormat = emailAttachmentFileFormat;
this.stretchyReportId = stretchyReportId;
this.stretchyReportParamMap = stretchyReportParamMap;
if(nextTriggerDate !=null){
this.nextTriggerDate = nextTriggerDate;
}else{
this.nextTriggerDate = null;
}
if(lastTriggerDate !=null){
this.lastTriggerDate = lastTriggerDate;
}else{
this.lastTriggerDate = null;
}
this.emailCampaignTimeLine =emailCampaignTimeLine;
this.recurrenceStartDate = recurrenceStartDate;
this.recurrence = recurrence;
}
public static EmailCampaignData instance(final Long id, final String campaignName, final Integer campaignType, final Long runReportId,
final String paramValue, final EnumOptionData campaignStatus, final String emailSubject,
final String message, final String emailAttachmentFileFormat, final Long stretchyReportId,
final String stretchyReportParamMap, final DateTime nextTriggerDate, final LocalDate lastTriggerDate,
final EmailCampaignTimeLine emailCampaignTimeLine,
final DateTime recurrenceStartDate, final String recurrence){
return new EmailCampaignData(id,campaignName,campaignType,runReportId,paramValue,
campaignStatus,emailSubject,message,emailAttachmentFileFormat,stretchyReportId,stretchyReportParamMap,nextTriggerDate,lastTriggerDate,emailCampaignTimeLine,recurrenceStartDate,recurrence);
}
public Long getId() {
return id;
}
public String getCampaignName() {
return this.campaignName;
}
public Integer getCampaignType() {
return this.campaignType;
}
public Long getRunReportId() {
return this.businessRuleId;
}
public String getParamValue() {
return this.paramValue;
}
public EnumOptionData getCampaignStatus() {
return this.campaignStatus;
}
public String getEmailSubject() { return this.emailSubject; }
public String getMessage() {
return this.emailMessage;
}
public DateTime getNextTriggerDate() {
return this.nextTriggerDate;
}
public LocalDate getLastTriggerDate() {
return this.lastTriggerDate;
}
public String getRecurrence() {return this.recurrence;}
public DateTime getRecurrenceStartDate() {return this.recurrenceStartDate;}
}
@@ -0,0 +1,40 @@
/**
* 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.data;
import org.joda.time.LocalDate;
public class EmailCampaignTimeLine {
private final LocalDate submittedOnDate;
private final String submittedByUsername;
private final LocalDate activatedOnDate;
private final String activatedByUsername;
private final LocalDate closedOnDate;
private final String closedByUsername;
public EmailCampaignTimeLine(final LocalDate submittedOnDate, final String submittedByUsername,
final LocalDate activatedOnDate, final String activatedByUsername, final LocalDate closedOnDate, final String closedByUsername) {
this.submittedOnDate = submittedOnDate;
this.submittedByUsername = submittedByUsername;
this.activatedOnDate = activatedOnDate;
this.activatedByUsername = activatedByUsername;
this.closedOnDate = closedOnDate;
this.closedByUsername = closedByUsername;
}
}
@@ -0,0 +1,259 @@
/**
* 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.data;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
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.campaigns.email.domain.EmailCampaignType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Type;
import java.util.*;
@Component
public class EmailCampaignValidator {
public static final String RESOURCE_NAME = "email";
public static final String campaignName = "campaignName";
public static final String campaignType = "campaignType";
public static final String businessRuleId = "businessRuleId";
public static final String stretchyReportId = "stretchyReportId";
public static final String stretchyReportParamMap = "stretchyReportParamMap";
public static final String paramValue = "paramValue";
public static final String emailSubject = "emailSubject";
public static final String emailMessage = "emailMessage";
public static final String emailAttachmentFileFormatId = "emailAttachmentFileFormatId";
public static final String activationDateParamName = "activationDate";
public static final String recurrenceStartDate = "recurrenceStartDate";
public static final String submittedOnDateParamName = "submittedOnDate";
public static final String closureDateParamName = "closureDate";
public static final String recurrenceParamName = "recurrence";
public static final String statusParamName = "status";
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
private final FromJsonHelper fromApiJsonHelper;
public static final Set<String> supportedParams = new HashSet<String>(Arrays.asList(campaignName, campaignType,localeParamName,dateFormatParamName,
businessRuleId,paramValue,emailMessage,recurrenceStartDate,activationDateParamName,submittedOnDateParamName,closureDateParamName,recurrenceParamName,
emailSubject,stretchyReportId,stretchyReportParamMap,emailAttachmentFileFormatId));
public static final Set<String> supportedParamsForUpdate = new HashSet<>(Arrays.asList(campaignName, campaignType,localeParamName,dateFormatParamName,
businessRuleId,paramValue,emailMessage,recurrenceStartDate,activationDateParamName,recurrenceParamName));
public static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, activationDateParamName));
public static final Set<String> CLOSE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
dateFormatParamName, closureDateParamName));
public static final Set<String> PREVIEW_REQUEST_DATA_PARAMETERS= new HashSet<String>(Arrays.asList(paramValue,emailMessage));
@Autowired
public EmailCampaignValidator(FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateCreate(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.supportedParams);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailCampaignValidator.RESOURCE_NAME);
final String campaignName = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.campaignName,element);
baseDataValidator.reset().parameter(EmailCampaignValidator.campaignName).value(campaignName).notBlank().notExceedingLengthOf(100);
final Long campaignType = this.fromApiJsonHelper.extractLongNamed(EmailCampaignValidator.campaignType,element);
baseDataValidator.reset().parameter(EmailCampaignValidator.campaignType).value(campaignType).notNull().integerGreaterThanZero();
if(campaignType.intValue() == EmailCampaignType.SCHEDULE.getValue()){
final String recurrenceParamName = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.recurrenceParamName, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.recurrenceParamName).value(recurrenceParamName).notBlank();
final String recurrenceStartDate = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.recurrenceStartDate, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.recurrenceStartDate).value(recurrenceStartDate).notBlank();
}
final Long businessRuleId = this.fromApiJsonHelper.extractLongNamed(EmailCampaignValidator.businessRuleId,element);
baseDataValidator.reset().parameter(EmailCampaignValidator.businessRuleId).value(businessRuleId).notNull().integerGreaterThanZero();
final String emailSubject = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.emailSubject, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.emailSubject).value(emailSubject).notBlank().notExceedingLengthOf(50);
final String emailMessage = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.emailMessage, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.emailMessage).value(emailMessage).notBlank().notExceedingLengthOf(480);
final String paramValue = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.paramValue, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.paramValue).value(paramValue).notBlank();
if (this.fromApiJsonHelper.parameterExists(EmailCampaignValidator.submittedOnDateParamName, element)) {
final LocalDate submittedOnDate = this.fromApiJsonHelper.extractLocalDateNamed(EmailCampaignValidator.submittedOnDateParamName,
element);
baseDataValidator.reset().parameter(EmailCampaignValidator.submittedOnDateParamName).value(submittedOnDate).notNull();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateForUpdate(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.supportedParamsForUpdate);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailCampaignValidator.RESOURCE_NAME);
final String campaignName = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.campaignName,element);
baseDataValidator.reset().parameter(EmailCampaignValidator.campaignName).value(campaignName).notBlank().notExceedingLengthOf(100);
final Long campaignType = this.fromApiJsonHelper.extractLongNamed(EmailCampaignValidator.campaignType,element);
baseDataValidator.reset().parameter(EmailCampaignValidator.campaignType).value(campaignType).notNull().integerGreaterThanZero();
if(campaignType.intValue() == EmailCampaignType.SCHEDULE.getValue()){
final String recurrenceParamName = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.recurrenceParamName, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.recurrenceParamName).value(recurrenceParamName).notBlank();
final String recurrenceStartDate = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.recurrenceStartDate, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.recurrenceStartDate).value(recurrenceStartDate).notBlank();
}
final Long runReportId = this.fromApiJsonHelper.extractLongNamed(EmailCampaignValidator.businessRuleId,element);
baseDataValidator.reset().parameter(EmailCampaignValidator.businessRuleId).value(runReportId).notNull().integerGreaterThanZero();
final String message = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.emailMessage, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.emailMessage).value(message).notBlank().notExceedingLengthOf(480);
final String paramValue = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.paramValue, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.paramValue).value(paramValue).notBlank();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validatePreviewMessage(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.PREVIEW_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailCampaignValidator.RESOURCE_NAME);
final String paramValue = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.paramValue, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.paramValue).value(paramValue).notBlank();
final String message = this.fromApiJsonHelper.extractStringNamed(EmailCampaignValidator.emailMessage, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.emailMessage).value(message).notBlank().notExceedingLengthOf(480);
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateClosedDate(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.CLOSE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailCampaignValidator.RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final LocalDate closeDate = this.fromApiJsonHelper.extractLocalDateNamed(EmailCampaignValidator.closureDateParamName, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.closureDateParamName).value(closeDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void validateActivation(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.ACTIVATION_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailCampaignValidator.RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final LocalDate activationDate = this.fromApiJsonHelper.extractLocalDateNamed(EmailCampaignValidator.activationDateParamName, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.activationDateParamName).value(activationDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void ValidateClosure(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.CLOSE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailCampaignValidator.RESOURCE_NAME);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final LocalDate closeDate = this.fromApiJsonHelper.extractLocalDateNamed(EmailCampaignValidator.closureDateParamName, element);
baseDataValidator.reset().parameter(EmailCampaignValidator.closureDateParamName).value(closeDate).notNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -0,0 +1,63 @@
/**
* 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.data;
/**
* Immutable data object representing an Email configuration.
*/
public class EmailConfigurationData {
@SuppressWarnings("unused")
private final Long id;
private final String name;
private final String value;
/**
* @return an instance of the EmailConfigurationData class
**/
public static EmailConfigurationData instance(Long id, String name, String value) {
return new EmailConfigurationData(id, name, value);
}
/**
* EmailConfigurationData constructor
**/
private EmailConfigurationData(Long id, String name, String value) {
this.id = id;
this.name = name;
this.value = value;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}
@@ -0,0 +1,111 @@
/**
* 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.data;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang.StringUtils;
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.campaigns.email.EmailApiConstants;
import org.apache.fineract.infrastructure.campaigns.email.exception.EmailConfigurationSMTPUsernameNotValid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class EmailConfigurationValidator {
private final FromJsonHelper fromApiJsonHelper;
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);
public static final Set<String> supportedParams = new HashSet<String>(Arrays.asList(EmailApiConstants.SMTP_PORT,EmailApiConstants.SMTP_PASSWORD,
EmailApiConstants.SMTP_USERNAME,EmailApiConstants.SMTP_SERVER));
@Autowired
private EmailConfigurationValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateUpdateConfiguration(String json){
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailConfigurationValidator.supportedParams);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(EmailApiConstants.RESOURCE_NAME);
final String smtpUsername = this.fromApiJsonHelper.extractStringNamed(EmailApiConstants.SMTP_USERNAME,element);
baseDataValidator.reset().parameter(EmailApiConstants.SMTP_USERNAME).value(smtpUsername).notBlank().notExceedingLengthOf(150);
final String smtpPassword = this.fromApiJsonHelper.extractStringNamed(EmailApiConstants.SMTP_PASSWORD,element);
baseDataValidator.reset().parameter(EmailApiConstants.SMTP_PASSWORD).value(smtpPassword).notBlank().notExceedingLengthOf(50);
final String smtpServer = this.fromApiJsonHelper.extractStringNamed(EmailApiConstants.SMTP_SERVER,element);
baseDataValidator.reset().parameter(EmailApiConstants.SMTP_SERVER).value(smtpServer).notBlank().notExceedingLengthOf(100);
final Long smtpPort = this.fromApiJsonHelper.extractLongNamed(EmailApiConstants.SMTP_PORT,element);
baseDataValidator.reset().parameter(EmailApiConstants.SMTP_PORT).value(smtpPort).notNull().integerGreaterThanZero();
this.throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
public boolean isValidEmail(String email) {
// this is the easiest check
if (email == null) {
return false;
}else 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;
}
}
@@ -0,0 +1,160 @@
/**
* 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.data;
import org.joda.time.LocalDate;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
import java.util.List;
/**
* Immutable data object representing a SMS message.
*/
public class EmailData {
private final Long id;
private final Long groupId;
private final Long clientId;
private final Long staffId;
private final EnumOptionData status;
private final String emailAddress;
private final String emailSubject;
private final String emailMessage;
private final EnumOptionData emailAttachmentFileFormat;
private final ReportData stretchyReport;
private final String stretchyReportParamMap;
private final List<EnumOptionData> emailAttachmentFileFormatOptions;
private final List<EnumOptionData> stretchyReportParamDateOptions;
private final String campaignName;
private final LocalDate sentDate;
private final String errorMessage;
public static EmailData instance(final Long id,final Long groupId, final Long clientId, final Long staffId, final EnumOptionData status,
final String emailAddress, final String emailSubject,
final String message, final EnumOptionData emailAttachmentFileFormat, final ReportData stretchyReport,
final String stretchyReportParamMap, final List<EnumOptionData> emailAttachmentFileFormatOptions,
final List<EnumOptionData> stretchyReportParamDateOptions, final String campaignName, final LocalDate sentDate,final String errorMessage) {
return new EmailData(id, groupId, clientId, staffId, status, emailAddress, emailSubject, message,
emailAttachmentFileFormat,stretchyReport,stretchyReportParamMap,emailAttachmentFileFormatOptions,
stretchyReportParamDateOptions,campaignName,sentDate,errorMessage);
}
private EmailData(final Long id,final Long groupId, final Long clientId, final Long staffId, final EnumOptionData status, final String emailAddress, final String emailSubject, final String message,
final EnumOptionData emailAttachmentFileFormat, final ReportData stretchyReport, final String stretchyReportParamMap,
final List<EnumOptionData> emailAttachmentFileFormatOptions, final List<EnumOptionData> stretchyReportParamDateOptions,
final String campaignName,final LocalDate sentDate,final String errorMessage) {
this.id = id;
this.groupId = groupId;
this.clientId = clientId;
this.staffId = staffId;
this.status = status;
this.emailAddress = emailAddress;
this.emailSubject = emailSubject;
this.emailMessage = message;
this.emailAttachmentFileFormat = emailAttachmentFileFormat;
this.stretchyReport = stretchyReport;
this.stretchyReportParamMap = stretchyReportParamMap;
this.emailAttachmentFileFormatOptions = emailAttachmentFileFormatOptions;
this.stretchyReportParamDateOptions = stretchyReportParamDateOptions;
this.campaignName = campaignName;
this.sentDate = sentDate;
this.errorMessage = errorMessage;
}
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @return the groupId
*/
public Long getGroupId() {
return groupId;
}
/**
* @return the clientId
*/
public Long getClientId() {
return clientId;
}
/**
* @return the staffId
*/
public Long getStaffId() {
return staffId;
}
/**
* @return the status
*/
public EnumOptionData getStatus() {
return status;
}
public String getErrorMessage() {return this.errorMessage;}
/**
* @return the emailAddress
*/
public String getEmailAddress() {
return emailAddress;
}
/**
* @return the message
*/
public String getMessage() {
return emailMessage;
}
public String getCampaignName() {return this.campaignName;}
public LocalDate getSentDate() { return this.sentDate; }
public String getEmailSubject() {
return emailSubject;
}
public EnumOptionData getEmailAttachmentFileFormat() {
return emailAttachmentFileFormat;
}
public ReportData getStretchyReport() {
return stretchyReport;
}
public String getStretchyReportParamMap() {
return stretchyReportParamMap;
}
public List<EnumOptionData> getEmailAttachmentFileFormatOptions() {
return emailAttachmentFileFormatOptions;
}
public List<EnumOptionData> getStretchyReportParamDateOptions() {
return stretchyReportParamDateOptions;
}
}
@@ -0,0 +1,307 @@
/**
* 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.data;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang.StringUtils;
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.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.campaigns.email.EmailApiConstants;
import org.apache.fineract.infrastructure.campaigns.email.ScheduledEmailConstants;
import org.apache.fineract.infrastructure.campaigns.email.domain.ScheduledEmailAttachmentFileFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public final class EmailDataValidator {
private final FromJsonHelper fromApiJsonHelper;
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 EmailDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
/**
* 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.fromApiJsonHelper.checkForUnsupportedParameters(typeToken, jsonString,
ScheduledEmailConstants.CREATE_REQUEST_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors).
resource(StringUtils.lowerCase(ScheduledEmailConstants.SCHEDULED_EMAIL_ENTITY_NAME));
final String name = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.NAME_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.NAME_PARAM_NAME).value(name).notBlank().notExceedingLengthOf(100);
final String startDateTime = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.START_DATE_TIME_PARAM_NAME,
jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.START_DATE_TIME_PARAM_NAME).value(startDateTime).notBlank();
final Integer stretchyReportId = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(ScheduledEmailConstants.STRETCHY_REPORT_ID_PARAM_NAME,
jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.STRETCHY_REPORT_ID_PARAM_NAME).value(stretchyReportId).notNull().
integerGreaterThanZero();
final String emailRecipients = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.EMAIL_RECIPIENTS_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_RECIPIENTS_PARAM_NAME).value(emailRecipients).notBlank();
final String emailSubject = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.EMAIL_SUBJECT_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_SUBJECT_PARAM_NAME).value(emailSubject).notBlank().notExceedingLengthOf(100);
final String emailMessage = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.EMAIL_MESSAGE_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_MESSAGE_PARAM_NAME).value(emailMessage).notBlank();
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.IS_ACTIVE_PARAM_NAME, jsonElement)) {
final Boolean isActive = this.fromApiJsonHelper.extractBooleanNamed(ScheduledEmailConstants.IS_ACTIVE_PARAM_NAME,
jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.IS_ACTIVE_PARAM_NAME).value(isActive).notNull();
}
final Integer emailAttachmentFileFormatId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).
value(emailAttachmentFileFormatId).notNull();
if (emailAttachmentFileFormatId != null) {
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).value(emailAttachmentFileFormatId).
isOneOfTheseValues(ScheduledEmailAttachmentFileFormat.validValues());
}
final String dateFormat = jsonCommand.dateFormat();
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.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(ScheduledEmailConstants.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.fromApiJsonHelper.checkForUnsupportedParameters(typeToken, jsonString,
ScheduledEmailConstants.UPDATE_REQUEST_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder dataValidatorBuilder = new DataValidatorBuilder(dataValidationErrors).
resource(StringUtils.lowerCase(ScheduledEmailConstants.SCHEDULED_EMAIL_ENTITY_NAME));
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.NAME_PARAM_NAME, jsonElement)) {
final String name = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.NAME_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.NAME_PARAM_NAME).value(name).notBlank().notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.STRETCHY_REPORT_ID_PARAM_NAME, jsonElement)) {
final Integer stretchyReportId = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(ScheduledEmailConstants.STRETCHY_REPORT_ID_PARAM_NAME,
jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.STRETCHY_REPORT_ID_PARAM_NAME).value(stretchyReportId).notNull().
integerGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.EMAIL_RECIPIENTS_PARAM_NAME, jsonElement)) {
final String emailRecipients = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.EMAIL_RECIPIENTS_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_RECIPIENTS_PARAM_NAME).value(emailRecipients).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.EMAIL_SUBJECT_PARAM_NAME, jsonElement)) {
final String emailSubject = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.EMAIL_SUBJECT_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_SUBJECT_PARAM_NAME).value(emailSubject).notBlank().notExceedingLengthOf(100);
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.EMAIL_MESSAGE_PARAM_NAME, jsonElement)) {
final String emailMessage = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.EMAIL_MESSAGE_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_MESSAGE_PARAM_NAME).value(emailMessage).notBlank();
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.IS_ACTIVE_PARAM_NAME, jsonElement)) {
final Boolean isActive = this.fromApiJsonHelper.extractBooleanNamed(ScheduledEmailConstants.IS_ACTIVE_PARAM_NAME,
jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.IS_ACTIVE_PARAM_NAME).value(isActive).notNull();
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, jsonElement)) {
final Integer emailAttachmentFileFormatId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME, jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).
value(emailAttachmentFileFormatId).notNull();
if (emailAttachmentFileFormatId != null) {
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME).value(emailAttachmentFileFormatId).
isOneOfTheseValues(ScheduledEmailAttachmentFileFormat.validValues());
}
}
if (this.fromApiJsonHelper.parameterExists(ScheduledEmailConstants.START_DATE_TIME_PARAM_NAME, jsonElement)) {
final String dateFormat = jsonCommand.dateFormat();
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.DATE_FORMAT_PARAM_NAME).value(dateFormat).notBlank();
final String startDateTime = this.fromApiJsonHelper.extractStringNamed(ScheduledEmailConstants.START_DATE_TIME_PARAM_NAME,
jsonElement);
dataValidatorBuilder.reset().parameter(ScheduledEmailConstants.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(ScheduledEmailConstants.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;
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -0,0 +1,53 @@
/**
* 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.data;
import java.io.File;
import java.util.List;
import java.util.Set;
public class EmailMessageWithAttachmentData {
private final String to;
private final String text;
private final String subject;
private final List<File> attachments;
private EmailMessageWithAttachmentData(final String to, final String text, final String subject, final List<File> attachments) {
this.to = to;
this.text = text;
this.subject = subject;
this.attachments = attachments;
}
public static EmailMessageWithAttachmentData createNew (final String to, final String text, final String subject, final List<File> attachments){
return new EmailMessageWithAttachmentData(to,text,subject,attachments);
}
public String getTo() {return this.to;}
public String getText() {return this.text;}
public String getSubject() {return this.subject;}
public List<File> getAttachments() {
return this.attachments;
}
}
@@ -0,0 +1,40 @@
/**
* 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.data;
public class PreviewCampaignMessage {
@SuppressWarnings("unused")
private final String campaignMessage;
private final Integer totalNumberOfMessages;
public PreviewCampaignMessage(String campaignMessage, Integer totalNumberOfMessages) {
this.campaignMessage = campaignMessage;
this.totalNumberOfMessages = totalNumberOfMessages;
}
public String getCampaignMessage() {
return campaignMessage;
}
public Integer getTotalNumberOfMessages() {
return totalNumberOfMessages;
}
}
@@ -0,0 +1,56 @@
/**
* 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.data;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.campaigns.email.domain.ScheduledEmailAttachmentFileFormat;
import org.apache.fineract.infrastructure.campaigns.email.domain.ScheduledEmailStretchyReportParamDateOption;
public class ScheduledEmailEnumerations {
public static EnumOptionData emailAttachementFileFormat(final Integer emailAttachementFileFormatId) {
return emailAttachementFileFormat(ScheduledEmailAttachmentFileFormat.instance(emailAttachementFileFormatId));
}
public static EnumOptionData emailAttachementFileFormat(final String emailAttachementFileFormatString) {
return emailAttachementFileFormat(ScheduledEmailAttachmentFileFormat.instance(emailAttachementFileFormatString));
}
public static EnumOptionData emailAttachementFileFormat(final ScheduledEmailAttachmentFileFormat emailAttachementFileFormat) {
EnumOptionData enumOptionData = null;
if (emailAttachementFileFormat != null) {
enumOptionData = new EnumOptionData(emailAttachementFileFormat.getId().longValue(), emailAttachementFileFormat.getCode(),
emailAttachementFileFormat.getValue());
}
return enumOptionData;
}
public static EnumOptionData stretchyReportDateOption(final ScheduledEmailStretchyReportParamDateOption
reportMailingJobStretchyReportParamDateOption) {
EnumOptionData enumOptionData = null;
if (reportMailingJobStretchyReportParamDateOption != null) {
enumOptionData = new EnumOptionData(reportMailingJobStretchyReportParamDateOption.getId().longValue(),
reportMailingJobStretchyReportParamDateOption.getCode(), reportMailingJobStretchyReportParamDateOption.getValue());
}
return enumOptionData;
}
}
@@ -0,0 +1,538 @@
/**
* 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.domain;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
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.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.dataqueries.domain.Report;
import org.apache.fineract.infrastructure.campaigns.email.ScheduledEmailConstants;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignValidator;
import org.apache.fineract.portfolio.client.api.ClientApiConstants;
import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import javax.persistence.*;
import java.security.InvalidParameterException;
import java.util.*;
@Entity
@Table(name = "scheduled_email_campaign")
public class EmailCampaign extends AbstractPersistableCustom<Long> {
@Column(name = "campaign_name", nullable = false)
private String campaignName;
@Column(name = "campaign_type", nullable = false)
private Integer campaignType;
@ManyToOne
@JoinColumn(name = "businessRule_id", nullable = false)
private Report businessRuleId ;
@Column(name = "param_value")
private String paramValue;
@Column(name = "status_enum", nullable = false)
private Integer status;
@Column(name = "email_subject", nullable = false)
private String emailSubject;
@Column(name = "email_message", nullable = false)
private String emailMessage;
@Column(name = "email_attachment_file_format", nullable = false)
private String emailAttachmentFileFormat;
@ManyToOne
@JoinColumn(name = "stretchy_report_id", nullable = false)
private Report stretchyReport;
@Column(name = "stretchy_report_param_map", nullable = true)
private String stretchyReportParamMap;
@Column(name = "closedon_date", nullable = true)
@Temporal(TemporalType.DATE)
private Date closureDate;
@ManyToOne(optional = true)
@JoinColumn(name = "closedon_userid", nullable = true)
private AppUser closedBy;
@Column(name = "submittedon_date", nullable = true)
@Temporal(TemporalType.DATE)
private Date submittedOnDate;
@ManyToOne(optional = true)
@JoinColumn(name = "submittedon_userid", nullable = true)
private AppUser submittedBy;
@Column(name = "approvedon_date", nullable = true)
@Temporal(TemporalType.DATE)
private Date approvedOnDate;
@ManyToOne(optional = true)
@JoinColumn(name = "approvedon_userid", nullable = true)
private AppUser approvedBy;
@Column(name = "recurrence", nullable = false)
private String recurrence;
@Column(name = "next_trigger_date", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date nextTriggerDate;
@Column(name = "last_trigger_date", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date lastTriggerDate;
@Column(name = "recurrence_start_date", nullable = false)
@Temporal(TemporalType.TIMESTAMP)
private Date recurrenceStartDate;
@Column(name="is_visible",nullable = true)
private boolean isVisible;
@Column(name = "previous_run_status", nullable = true)
private String previousRunStatus;
@Column(name = "previous_run_error_log", nullable = true)
private String previousRunErrorLog;
@Column(name = "previous_run_error_message", nullable = true)
private String previousRunErrorMessage;
public EmailCampaign() {
}
private EmailCampaign(final String campaignName, final Integer campaignType, final Report businessRuleId, final String paramValue,
final String emailSubject, final String emailMessage,final LocalDate submittedOnDate, final AppUser submittedBy,
final Report stretchyReport, final String stretchyReportParamMap, final ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat,
final String recurrence, final LocalDateTime localDateTime) {
this.campaignName = campaignName;
this.campaignType = EmailCampaignType.fromInt(campaignType).getValue();
this.businessRuleId = businessRuleId;
this.paramValue = paramValue;
this.status = EmailCampaignStatus.PENDING.getValue();
this.emailSubject = emailSubject;
this.emailMessage = emailMessage;
this.emailAttachmentFileFormat = emailAttachmentFileFormat.getValue();
this.stretchyReport = stretchyReport;
this.stretchyReportParamMap = stretchyReportParamMap;
this.submittedOnDate = submittedOnDate.toDate();
this.submittedBy = submittedBy;
this.recurrence = recurrence;
LocalDateTime recurrenceStartDate = new LocalDateTime();
this.isVisible = true;
if(localDateTime != null){
this.recurrenceStartDate = localDateTime.toDate();
}else{
this.recurrenceStartDate = recurrenceStartDate.toDate();
}
}
public static EmailCampaign instance(final AppUser submittedBy, final Report businessRuleId, final Report stretchyReport, final JsonCommand command){
final String campaignName = command.stringValueOfParameterNamed(EmailCampaignValidator.campaignName);
final Long campaignType = command.longValueOfParameterNamed(EmailCampaignValidator.campaignType);
final String paramValue = command.stringValueOfParameterNamed(EmailCampaignValidator.paramValue);
final String emailSubject = command.stringValueOfParameterNamed(EmailCampaignValidator.emailSubject);
final String emailMessage = command.stringValueOfParameterNamed(EmailCampaignValidator.emailMessage);
final String stretchyReportParamMap = command.stringValueOfParameterNamed(ScheduledEmailConstants.STRETCHY_REPORT_PARAM_MAP_PARAM_NAME);
final Integer emailAttachmentFileFormatId = command.integerValueOfParameterNamed(ScheduledEmailConstants.EMAIL_ATTACHMENT_FILE_FORMAT_ID_PARAM_NAME);
final ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat;
if (emailAttachmentFileFormatId!=null) {
emailAttachmentFileFormat = ScheduledEmailAttachmentFileFormat.instance(emailAttachmentFileFormatId);
}else{emailAttachmentFileFormat = ScheduledEmailAttachmentFileFormat.instance(2);}
LocalDate submittedOnDate = new LocalDate();
if (command.hasParameter(EmailCampaignValidator.submittedOnDateParamName)) {
submittedOnDate = command.localDateValueOfParameterNamed(EmailCampaignValidator.submittedOnDateParamName);
}
final String recurrence = command.stringValueOfParameterNamed(EmailCampaignValidator.recurrenceParamName);
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
LocalDateTime recurrenceStartDate = new LocalDateTime();
if(EmailCampaignType.fromInt(campaignType.intValue()).isSchedule()) {
if(command.hasParameter(EmailCampaignValidator.recurrenceStartDate)){
recurrenceStartDate = LocalDateTime.parse(command.stringValueOfParameterNamed(EmailCampaignValidator.recurrenceStartDate),fmt);
}
} else{recurrenceStartDate = null;}
return new EmailCampaign(campaignName,campaignType.intValue(),businessRuleId,paramValue,emailSubject,emailMessage,
submittedOnDate,submittedBy,stretchyReport,stretchyReportParamMap,emailAttachmentFileFormat,recurrence,recurrenceStartDate);
}
public Map<String,Object> update(JsonCommand command){
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
if (command.isChangeInStringParameterNamed(EmailCampaignValidator.campaignName, this.campaignName)) {
final String newValue = command.stringValueOfParameterNamed(EmailCampaignValidator.campaignName);
actualChanges.put(EmailCampaignValidator.campaignName, newValue);
this.campaignName = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInStringParameterNamed(EmailCampaignValidator.emailMessage, this.emailMessage)) {
final String newValue = command.stringValueOfParameterNamed(EmailCampaignValidator.emailMessage);
actualChanges.put(EmailCampaignValidator.emailMessage, newValue);
this.emailMessage = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInStringParameterNamed(EmailCampaignValidator.paramValue, this.paramValue)) {
final String newValue = command.stringValueOfParameterNamed(EmailCampaignValidator.paramValue);
actualChanges.put(EmailCampaignValidator.paramValue, newValue);
this.paramValue = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInIntegerParameterNamed(EmailCampaignValidator.campaignType, this.campaignType)) {
final Integer newValue = command.integerValueOfParameterNamed(EmailCampaignValidator.campaignType);
actualChanges.put(EmailCampaignValidator.campaignType, EmailCampaignType.fromInt(newValue));
this.campaignType = EmailCampaignType.fromInt(newValue).getValue();
}
if (command.isChangeInLongParameterNamed(EmailCampaignValidator.businessRuleId, (this.businessRuleId != null) ? this.businessRuleId.getId() : null)) {
final String newValue = command.stringValueOfParameterNamed(EmailCampaignValidator.businessRuleId);
actualChanges.put(EmailCampaignValidator.businessRuleId, newValue);
}
if (command.isChangeInStringParameterNamed(EmailCampaignValidator.recurrenceParamName, this.recurrence)) {
final String newValue = command.stringValueOfParameterNamed(EmailCampaignValidator.recurrenceParamName);
actualChanges.put(EmailCampaignValidator.recurrenceParamName, newValue);
this.recurrence = StringUtils.defaultIfEmpty(newValue, null);
}
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormat.forPattern(command.dateFormat()).withLocale(locale);
if (command.isChangeInLocalDateParameterNamed(EmailCampaignValidator.recurrenceStartDate, getRecurrenceStartDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(EmailCampaignValidator.recurrenceStartDate);
actualChanges.put(EmailCampaignValidator.recurrenceStartDate, valueAsInput);
actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
final LocalDateTime newValue = LocalDateTime.parse(valueAsInput, fmt);
this.recurrenceStartDate = newValue.toDate();
}
return actualChanges;
}
public void activate(final AppUser currentUser, final DateTimeFormatter formatter, final LocalDate activationLocalDate){
if(isActive()){
//handle errors if already activated
final String defaultUserMessage = "Cannot activate campaign. Campaign is already active.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.already.active", defaultUserMessage,
EmailCampaignValidator.activationDateParamName, activationLocalDate.toString(formatter));
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
this.approvedOnDate = activationLocalDate.toDate();
this.approvedBy = currentUser;
this.status = EmailCampaignStatus.ACTIVE.getValue();
validate();
}
public void close(final AppUser currentUser,final DateTimeFormatter dateTimeFormatter, final LocalDate closureLocalDate){
if(isClosed()){
//handle errors if already activated
final String defaultUserMessage = "Cannot close campaign. Campaign already in closed state.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.already.closed", defaultUserMessage,
EmailCampaignValidator.statusParamName, EmailCampaignStatus.fromInt(this.status).getCode());
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
if(this.campaignType.intValue() == EmailCampaignType.SCHEDULE.getValue()){
this.nextTriggerDate = null;
this.lastTriggerDate = null;
}
this.closedBy = currentUser;
this.closureDate = closureLocalDate.toDate();
this.status = EmailCampaignStatus.CLOSED.getValue();
validateClosureDate();
}
public void reactivate(final AppUser currentUser, final DateTimeFormatter dateTimeFormat,final LocalDate reactivateLocalDate){
if(!isClosed()){
//handle errors if already activated
final String defaultUserMessage = "Cannot reactivate campaign. Campaign must be in closed state.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.must.be.closed", defaultUserMessage,
EmailCampaignValidator.statusParamName, EmailCampaignStatus.fromInt(this.status).getCode());
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
this.approvedOnDate = reactivateLocalDate.toDate();
this.status = EmailCampaignStatus.ACTIVE.getValue();
this.approvedBy =currentUser;
this.closureDate = null;
this.isVisible = true;
this.closedBy = null;
validateReactivate();
}
public void delete(){
if(!isClosed()){
//handle errors if already activated
final String defaultUserMessage = "Cannot delete campaign. Campaign must be in closed state.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.must.be.closed", defaultUserMessage,
EmailCampaignValidator.statusParamName, EmailCampaignStatus.fromInt(this.status).getCode());
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
this.isVisible = false;
}
public boolean isActive(){
return EmailCampaignStatus.fromInt(this.status).isActive();
}
public boolean isPending(){
return EmailCampaignStatus.fromInt(this.status).isPending();
}
public boolean isClosed(){
return EmailCampaignStatus.fromInt(this.status).isClosed();
}
public boolean isDirect(){
return EmailCampaignType.fromInt(this.campaignType).isDirect();
}
public boolean isSchedule(){
return EmailCampaignType.fromInt(this.campaignType).isSchedule();
}
private void validate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
validateActivationDate(dataValidationErrors);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void validateReactivate(){
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
validateReactivationDate(dataValidationErrors);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void validateClosureDate(){
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
validateClosureDate(dataValidationErrors);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) {
if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) {
final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error);
}
if (getActivationLocalDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getActivationLocalDate())) {
final String defaultUserMessage = "submitted date cannot be after the activation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error);
}
if (getActivationLocalDate() != null && isDateInTheFuture(getActivationLocalDate())) {
final String defaultUserMessage = "Activation date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.activationDateParamName, getActivationLocalDate());
dataValidationErrors.add(error);
}
}
private void validateReactivationDate(final List<ApiParameterError> dataValidationErrors){
if (getActivationLocalDate() != null && isDateInTheFuture(getActivationLocalDate())) {
final String defaultUserMessage = "Activation date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.activationDateParamName, getActivationLocalDate());
dataValidationErrors.add(error);
}
if (getActivationLocalDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getActivationLocalDate())) {
final String defaultUserMessage = "submitted date cannot be after the activation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error);
}
if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) {
final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error);
}
}
private void validateClosureDate(final List<ApiParameterError> dataValidationErrors){
if (getClosureDate() != null && isDateInTheFuture(getClosureDate())) {
final String defaultUserMessage = "closure date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.closureDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.closureDateParamName, this.closureDate);
dataValidationErrors.add(error);
}
}
public LocalDate getSubmittedOnDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.submittedOnDate), null);
}
public LocalDate getClosureDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.closureDate), null);
}
public LocalDate getActivationLocalDate() {
LocalDate activationLocalDate = null;
if (this.approvedOnDate != null) {
activationLocalDate = LocalDate.fromDateFields(this.approvedOnDate);
}
return activationLocalDate;
}
private boolean isDateInTheFuture(final LocalDate localDate) {
return localDate.isAfter(DateUtils.getLocalDateOfTenant());
}
public Report getBusinessRuleId() {
return this.businessRuleId;
}
public String getCampaignName() {
return this.campaignName;
}
public String getEmailSubject() { return this.emailSubject; }
public String getEmailMessage() {
return this.emailMessage;
}
public String getParamValue() {
return this.paramValue;
}
public String getRecurrence() {
return this.recurrence;
}
public LocalDate getRecurrenceStartDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.recurrenceStartDate), null);
}
public LocalDateTime getRecurrenceStartDateTime() {
return (LocalDateTime) ObjectUtils.defaultIfNull(new LocalDateTime(this.recurrenceStartDate), null);
}
public void setLastTriggerDate(Date lastTriggerDate) {
this.lastTriggerDate = lastTriggerDate;
}
public void setNextTriggerDate(Date nextTriggerDate) {
this.nextTriggerDate = nextTriggerDate;
}
public LocalDateTime getNextTriggerDate() {
return (LocalDateTime) ObjectUtils.defaultIfNull(new LocalDateTime(this.nextTriggerDate), null);
}
public Date getNextTriggerDateInDate(){
return this.nextTriggerDate;
}
public LocalDate getLastTriggerDate() {
return (LocalDate) ObjectUtils.defaultIfNull(new LocalDate(this.lastTriggerDate), null);
}
public void updateIsVisible(boolean isVisible) {
this.isVisible = isVisible;
}
public void updateBusinessRuleId(final Report report){
this.businessRuleId = report;
}
public String getEmailAttachmentFileFormat() {
return this.emailAttachmentFileFormat;
}
public Report getStretchyReport() {
return this.stretchyReport;
}
public String getStretchyReportParamMap() {
return this.stretchyReportParamMap;
}
public void setStretchyReportParamMap(String stretchyReportParamMap) {
this.stretchyReportParamMap = stretchyReportParamMap;
}
public AppUser getApprovedBy() {return this.approvedBy;}
public AppUser getClosedBy() {
return this.closedBy;
}
}
@@ -0,0 +1,25 @@
/**
* 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.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface EmailCampaignRepository extends JpaRepository<EmailCampaign, Long>, JpaSpecificationExecutor<EmailCampaign> {
}
@@ -0,0 +1,71 @@
/**
* 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.domain;
public enum EmailCampaignStatus {
INVALID(0, "emailCampaignStatus.invalid"), //
PENDING(100, "emailCampaignStatus.pending"), //
ACTIVE(300, "emailCampaignStatus.active"), //
CLOSED(600, "emailCampaignStatus.closed");
private final Integer value;
private final String code;
EmailCampaignStatus(Integer value, String code) {
this.value = value;
this.code = code;
}
public static EmailCampaignStatus fromInt(final Integer statusValue) {
EmailCampaignStatus enumeration = EmailCampaignStatus.INVALID;
switch (statusValue) {
case 100:
enumeration = EmailCampaignStatus.PENDING;
break;
case 300:
enumeration = EmailCampaignStatus.ACTIVE;
break;
case 600:
enumeration = EmailCampaignStatus.CLOSED;
break;
}
return enumeration;
}
public Integer getValue() {
return value;
}
public String getCode() {
return code;
}
public boolean isActive(){
return this.value.equals(EmailCampaignStatus.ACTIVE.getValue());
}
public boolean isPending(){
return this.value.equals(EmailCampaignStatus.PENDING.getValue());
}
public boolean isClosed(){
return this.value.equals(EmailCampaignStatus.CLOSED.getValue());
}
}
@@ -0,0 +1,53 @@
/**
* 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.domain;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public class EmailCampaignStatusEnumerations {
public static EnumOptionData status(final Integer statusId) {
return status(EmailCampaignStatus.fromInt(statusId));
}
public static EnumOptionData status(final EmailCampaignStatus status) {
EnumOptionData optionData = new EnumOptionData(EmailCampaignStatus.INVALID.getValue().longValue(),
EmailCampaignStatus.INVALID.getCode(), "Invalid");
switch (status) {
case INVALID:
optionData = new EnumOptionData(EmailCampaignStatus.INVALID.getValue().longValue(),
EmailCampaignStatus.INVALID.getCode(), "Invalid");
break;
case PENDING:
optionData = new EnumOptionData(EmailCampaignStatus.PENDING.getValue().longValue(),
EmailCampaignStatus.PENDING.getCode(), "Pending");
break;
case ACTIVE:
optionData = new EnumOptionData(EmailCampaignStatus.ACTIVE.getValue().longValue(), EmailCampaignStatus.ACTIVE.getCode(),
"active");
break;
case CLOSED:
optionData = new EnumOptionData(EmailCampaignStatus.CLOSED.getValue().longValue(),
EmailCampaignStatus.CLOSED.getCode(), "closed");
break;
}
return optionData;
}
}
@@ -0,0 +1,61 @@
/**
* 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.domain;
public enum EmailCampaignType {
DIRECT(1,"emailCampaignStatusType.direct"),
SCHEDULE(2,"emailCampaignStatusType.schedule");
private Integer value;
private String code;
EmailCampaignType(Integer value, String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return value;
}
public String getCode() {
return code;
}
public static EmailCampaignType fromInt(final Integer typeValue) {
EmailCampaignType type = null;
switch (typeValue) {
case 1:
type = DIRECT;
break;
case 2:
type = SCHEDULE;
break;
}
return type;
}
public boolean isDirect(){
return this.value.equals(EmailCampaignType.DIRECT.getValue());
}
public boolean isSchedule(){
return this.value.equals(EmailCampaignType.SCHEDULE.getValue());
}
}
@@ -0,0 +1,60 @@
/**
* 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.domain;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "scheduled_email_configuration")
public class EmailConfiguration extends AbstractPersistableCustom<Long> {
@Column(name = "name", nullable = false)
private String name;
@Column(name = "value", nullable = false)
private String value;
/**
* EmailConfiguration constructor
**/
protected EmailConfiguration() {}
/**
* EmailConfiguration constructor
**/
public EmailConfiguration(String name, String value) {
this.name = name;
this.value = value;
}
public void setValue(String value) {
this.value = value;
}
public String getName() {
return this.name;
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,26 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.campaigns.email.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface EmailConfigurationRepository extends JpaRepository<EmailConfiguration, Long>, JpaSpecificationExecutor<EmailConfiguration> {
EmailConfiguration findByName(String name);
}
@@ -0,0 +1,164 @@
/**
* 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.domain;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.LocalDate;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.campaigns.email.EmailApiConstants;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import javax.persistence.*;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
@Entity
@Table(name = "scheduled_email_messages_outbound")
public class EmailMessage extends AbstractPersistableCustom<Long> {
@ManyToOne
@JoinColumn(name = "group_id", nullable = true)
private Group group;
@ManyToOne
@JoinColumn(name = "client_id", nullable = true)
private Client client;
@ManyToOne
@JoinColumn(name = "staff_id", nullable = true)
private Staff staff;
@ManyToOne
@JoinColumn(name = "email_campaign_id", nullable = true)
private EmailCampaign emailCampaign;
@Column(name = "status_enum", nullable = false)
private Integer statusType;
@Column(name = "email_address", nullable = false, length = 50)
private String emailAddress;
@Column(name = "email_subject", nullable = false, length = 50)
private String emailSubject;
@Column(name = "message", nullable = false)
private String message;
@Column(name = "campaign_name", nullable = true)
private String campaignName;
@Column(name = "submittedon_date", nullable = true)
@Temporal(TemporalType.DATE)
private Date submittedOnDate;
@Column(name = "error_message")
private String errorMessage;
public static EmailMessage pendingEmail(final Group group, final Client client, final Staff staff,final EmailCampaign emailCampaign, final String emailSubject, final String message,
final String emailAddress, final String campaignName) {
return new EmailMessage(group, client, staff,emailCampaign,EmailMessageStatusType.PENDING, emailSubject, message, emailAddress,campaignName);
}
public static EmailMessage instance(final Group group, final Client client, final Staff staff, final EmailCampaign emailCampaign, final EmailMessageStatusType statusType,
final String emailSubject, final String message, final String sourceAddress, final String emailAddress, final String campaignName) {
return new EmailMessage(group, client, staff,emailCampaign, statusType, emailSubject, message, emailAddress, campaignName);
}
protected EmailMessage() {
//
}
private EmailMessage(final Group group, final Client client, final Staff staff, final EmailCampaign emailCampaign, final EmailMessageStatusType statusType,
final String emailSubject, final String message, final String emailAddress, final String campaignName) {
this.group = group;
this.client = client;
this.staff = staff;
this.emailCampaign = emailCampaign;
this.statusType = statusType.getValue();
this.emailAddress = emailAddress;
this.emailSubject = emailSubject;
this.message = message;
this.campaignName = campaignName;
this.submittedOnDate = LocalDate.now().toDate();
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(1);
if (command.isChangeInStringParameterNamed(EmailApiConstants.messageParamName, this.message)) {
final String newValue = command.stringValueOfParameterNamed(EmailApiConstants.messageParamName);
actualChanges.put(EmailApiConstants.messageParamName, newValue);
this.message = StringUtils.defaultIfEmpty(newValue, null);
}
return actualChanges;
}
public Group getGroup() {
return group;
}
public Client getClient() {
return client;
}
public Staff getStaff() {
return staff;
}
public Integer getStatusType() {
return statusType;
}
public String getEmailAddress() {return this.emailAddress;}
public String getEmailSubject() {return emailSubject; }
public String getMessage() {
return message;
}
public void setStatusType(final Integer statusType) {
this.statusType = statusType;
}
public String getCampaignName() {
return this.campaignName;
}
public Date getSubmittedOnDate() {
return this.submittedOnDate;
}
public EmailCampaign getEmailCampaign() {return this.emailCampaign;}
public void updateErrorMessage(final String errorMessage) {this.errorMessage = errorMessage;}
public boolean isPending(){ return EmailMessageStatusType.fromInt(this.statusType).isPending();}
public boolean isSent(){ return EmailMessageStatusType.fromInt(this.statusType).isSent();}
}
@@ -0,0 +1,92 @@
/**
* 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.domain;
import com.google.gson.JsonElement;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.campaigns.email.EmailApiConstants;
import org.apache.fineract.infrastructure.campaigns.email.exception.EmailNotFoundException;
import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.organisation.staff.domain.StaffRepositoryWrapper;
import org.apache.fineract.portfolio.client.domain.Client;
import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper;
import org.apache.fineract.portfolio.group.domain.Group;
import org.apache.fineract.portfolio.group.domain.GroupRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class EmailMessageAssembler {
private final EmailMessageRepository emailMessageRepository;
private final GroupRepositoryWrapper groupRepository;
private final ClientRepositoryWrapper clientRepository;
private final StaffRepositoryWrapper staffRepository;
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public EmailMessageAssembler(final EmailMessageRepository emailMessageRepository, final GroupRepositoryWrapper groupRepositoryWrapper,
final ClientRepositoryWrapper clientRepository, final StaffRepositoryWrapper staffRepository,
final FromJsonHelper fromApiJsonHelper) {
this.emailMessageRepository = emailMessageRepository;
this.groupRepository = groupRepositoryWrapper;
this.clientRepository = clientRepository;
this.staffRepository = staffRepository;
this.fromApiJsonHelper = fromApiJsonHelper;
}
public EmailMessage assembleFromJson(final JsonCommand command) {
final JsonElement element = command.parsedJson();
String emailAddress = null;
Group group = null;
if (this.fromApiJsonHelper.parameterExists(EmailApiConstants.groupIdParamName, element)) {
final Long groupId = this.fromApiJsonHelper.extractLongNamed(EmailApiConstants.groupIdParamName, element);
group = this.groupRepository.findOneWithNotFoundDetection(groupId);
}
Client client = null;
if (this.fromApiJsonHelper.parameterExists(EmailApiConstants.clientIdParamName, element)) {
final Long clientId = this.fromApiJsonHelper.extractLongNamed(EmailApiConstants.clientIdParamName, element);
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
emailAddress = client.emailAddress();
}
Staff staff = null;
if (this.fromApiJsonHelper.parameterExists(EmailApiConstants.staffIdParamName, element)) {
final Long staffId = this.fromApiJsonHelper.extractLongNamed(EmailApiConstants.staffIdParamName, element);
staff = this.staffRepository.findOneWithNotFoundDetection(staffId);
emailAddress = staff.emailAddress();
}
final String message = this.fromApiJsonHelper.extractStringNamed(EmailApiConstants.messageParamName, element);
final String emailSubject = this.fromApiJsonHelper.extractStringNamed(EmailApiConstants.subjectParamName, element);
return EmailMessage.pendingEmail(group, client, staff,null,emailSubject, message,emailAddress,null);
}
public EmailMessage assembleFromResourceId(final Long resourceId) {
final EmailMessage email = this.emailMessageRepository.findOne(resourceId);
if (email == null) { throw new EmailNotFoundException(resourceId); }
return email;
}
}
@@ -0,0 +1,58 @@
/**
* 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.domain;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
public class EmailMessageEnumerations {
public static EnumOptionData status(final Integer statusId) {
return status(EmailMessageStatusType.fromInt(statusId));
}
public static EnumOptionData status(final EmailMessageStatusType status) {
EnumOptionData optionData = new EnumOptionData(EmailMessageStatusType.INVALID.getValue().longValue(),
EmailMessageStatusType.INVALID.getCode(), "Invalid");
switch (status) {
case INVALID:
optionData = new EnumOptionData(EmailMessageStatusType.INVALID.getValue().longValue(),
EmailMessageStatusType.INVALID.getCode(), "Invalid");
break;
case PENDING:
optionData = new EnumOptionData(EmailMessageStatusType.PENDING.getValue().longValue(),
EmailMessageStatusType.PENDING.getCode(), "Pending");
break;
case SENT:
optionData = new EnumOptionData(EmailMessageStatusType.SENT.getValue().longValue(), EmailMessageStatusType.SENT.getCode(),
"Sent");
break;
case DELIVERED:
optionData = new EnumOptionData(EmailMessageStatusType.DELIVERED.getValue().longValue(),
EmailMessageStatusType.DELIVERED.getCode(), "Delivered");
break;
case FAILED:
optionData = new EnumOptionData(EmailMessageStatusType.FAILED.getValue().longValue(), EmailMessageStatusType.FAILED.getCode(),
"Failed");
break;
}
return optionData;
}
}
@@ -0,0 +1,29 @@
/**
* 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.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
public interface EmailMessageRepository extends JpaRepository<EmailMessage, Long>, JpaSpecificationExecutor<EmailMessage> {
// no extra behaviour
List<EmailMessage> findByStatusType(Integer emailMessageStatus);
}
@@ -0,0 +1,72 @@
/**
* 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.domain;
public enum EmailMessageStatusType {
INVALID(0, "emailMessageStatusType.invalid"), //
PENDING(100, "emailMessageStatusType.pending"), //
SENT(200, "emailMessageStatusType.sent"), //
DELIVERED(300, "emailMessageStatusType.delivered"), //
FAILED(400, "emailMessageStatusType.failed");
private final Integer value;
private final String code;
public static EmailMessageStatusType fromInt(final Integer statusValue) {
EmailMessageStatusType enumeration = EmailMessageStatusType.INVALID;
switch (statusValue) {
case 100:
enumeration = EmailMessageStatusType.PENDING;
break;
case 200:
enumeration = EmailMessageStatusType.SENT;
break;
case 300:
enumeration = EmailMessageStatusType.DELIVERED;
break;
case 400:
enumeration = EmailMessageStatusType.FAILED;
break;
}
return enumeration;
}
EmailMessageStatusType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public boolean isPending(){
return this.value.equals(EmailMessageStatusType.PENDING.getValue());
}
public boolean isSent(){
return this.value.equals(EmailMessageStatusType.SENT.getValue());
}
}
@@ -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.domain;
public enum ScheduledEmailAttachmentFileFormat {
INVALID(0, "EmailAttachmentFileFormat.invalid", "invalid"),
XLS(1, "EmailAttachmentFileFormat.xls", "xls"),
PDF(2, "EmailAttachmentFileFormat.pdf", "pdf"),
CSV(3, "EmailAttachmentFileFormat.csv", "csv");
private String code;
private String value;
private Integer id;
ScheduledEmailAttachmentFileFormat(final Integer id, final String code, final String value) {
this.value = value;
this.code = code;
this.id = id;
}
public static ScheduledEmailAttachmentFileFormat instance(final String value) {
ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat = INVALID;
switch (value) {
case "xls":
emailAttachmentFileFormat = XLS;
break;
case "pdf":
emailAttachmentFileFormat = PDF;
break;
case "csv":
emailAttachmentFileFormat = CSV;
break;
default:
break;
}
return emailAttachmentFileFormat;
}
public static ScheduledEmailAttachmentFileFormat instance(final Integer id) {
ScheduledEmailAttachmentFileFormat emailAttachmentFileFormat = INVALID;
switch (id) {
case 1:
emailAttachmentFileFormat = XLS;
break;
case 2:
emailAttachmentFileFormat = PDF;
break;
case 3:
emailAttachmentFileFormat = CSV;
break;
default:
break;
}
return emailAttachmentFileFormat;
}
/**
* @return the code
*/
public String getCode() {
return code;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @return the id
*/
public Integer getId() {
return id;
}
/**
* @return list of valid ScheduledEmailAttachmentFileFormat ids
**/
public static Object[] validValues() {
return new Object[] { XLS.getId(), PDF.getId(), CSV.getId() };
}
}
@@ -0,0 +1,117 @@
/**
* 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.domain;
public enum ScheduledEmailStretchyReportParamDateOption {
INVALID(0, "scheduledEmailStretchyReportParamDateOption.invalid", "invalid"),
TODAY(1, "scheduledEmailStretchyReportParamDateOption.today", "today"),
// YESTERDAY(2, "scheduledEmailStretchyReportParamDateOption.yesterday", "yesterday"),
TOMORROW(3, "scheduledEmailStretchyReportParamDateOption.tomorrow", "tomorrow");
private String code;
private String value;
private Integer id;
/**
* @param id
* @param code
* @param value
*/
private ScheduledEmailStretchyReportParamDateOption(final Integer id, final String code, final String value) {
this.value = value;
this.code = code;
this.id = id;
}
/**
* @param value
* @return
*/
public static ScheduledEmailStretchyReportParamDateOption instance(final String value) {
ScheduledEmailStretchyReportParamDateOption scheduledEmailStretchyReportParamDateOption = INVALID;
switch (value) {
case "today":
scheduledEmailStretchyReportParamDateOption = TODAY;
break;
// case "yesterday":
// scheduledEmailStretchyReportParamDateOption = YESTERDAY;
// break;
case "tomorrow":
scheduledEmailStretchyReportParamDateOption = TOMORROW;
break;
}
return scheduledEmailStretchyReportParamDateOption;
}
/**
* @param id
* @return
*/
public static ScheduledEmailStretchyReportParamDateOption instance(final Integer id) {
ScheduledEmailStretchyReportParamDateOption scheduledEmailStretchyReportParamDateOption = INVALID;
switch (id) {
case 1:
scheduledEmailStretchyReportParamDateOption = TODAY;
break;
// case 2:
// scheduledEmailStretchyReportParamDateOption = YESTERDAY;
// break;
case 3:
scheduledEmailStretchyReportParamDateOption = TOMORROW;
break;
}
return scheduledEmailStretchyReportParamDateOption;
}
/**
* @return the code
*/
public String getCode() {
return code;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @return the id
*/
public Integer getId() {
return id;
}
/**
* @return list of valid ScheduledEmailAttachmentFileFormat values
**/
public static Object[] validValues() {
return new Object[] { TODAY.value, TOMORROW.value };
}
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class EmailBusinessRuleNotFound extends AbstractPlatformResourceNotFoundException {
public EmailBusinessRuleNotFound(final Long resourceId) {
super("error.msg.email.business.rule.not.found", "Email business rule with identifier `" + resourceId + "` does not exist", resourceId);
}
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class EmailCampaignMustBeClosedToBeDeletedException extends AbstractPlatformDomainRuleException {
public EmailCampaignMustBeClosedToBeDeletedException(final Long resourceId) {
super("error.msg.email.campaign.cannot.be.deleted",
"Campaign with identifier " + resourceId + " cannot be deleted as it is not in `Closed` state.", resourceId); }
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class EmailCampaignMustBeClosedToEditException extends AbstractPlatformDomainRuleException {
public EmailCampaignMustBeClosedToEditException(final Long resourceId) {
super("error.msg.email.campaign.cannot.be.updated",
"Campaign with identifier " + resourceId + " cannot be updated as it is not in `Closed` state.", resourceId);
}
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class EmailCampaignNotFound extends AbstractPlatformResourceNotFoundException{
public EmailCampaignNotFound(final Long resourceId) {
super("error.msg.email.campaign.identifier.not.found", "EMAIL_CAMPAIGN with identifier `" + resourceId + "` does not exist", resourceId);
}
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when a code is not found.
*/
public class EmailConfigurationNotFoundException extends AbstractPlatformResourceNotFoundException {
public EmailConfigurationNotFoundException(final String name) {
super("error.msg.email.configuration.name.not.found", "Email configuration with name " + name + " does not exist", name);
}
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class EmailConfigurationSMTPUsernameNotValid extends AbstractPlatformDomainRuleException {
public EmailConfigurationSMTPUsernameNotValid(final String smtpUsername) {
super("error.msg.email.configuration.smtpusername.not.valid",
"SMTP username configuration with email " + "'"+smtpUsername+"'" + " is not a valid email address ");
}
}
@@ -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.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when a code is not found.
*/
public class EmailNotFoundException extends AbstractPlatformResourceNotFoundException {
public EmailNotFoundException(final Long resourceId) {
super("error.msg.email.identifier.not.found", "Email with identifier `" + resourceId + "` does not exist", resourceId);
}
}
@@ -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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL_CAMPAIGN", action = "ACTIVATE")
public class ActivateEmailCampaignCommandHandler implements NewCommandSourceHandler {
private EmailCampaignWritePlatformService emailCampaignWritePlatformService;
@Autowired
public ActivateEmailCampaignCommandHandler(final EmailCampaignWritePlatformService emailCampaignWritePlatformService) {
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.emailCampaignWritePlatformService.activateEmailCampaign(command.entityId(), command);
}
}
@@ -0,0 +1,43 @@
/**
* 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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@CommandType(entity = "EMAIL_CAMPAIGN", action = "CLOSE")
public class CloseEmailCampaignCommandHandler implements NewCommandSourceHandler {
private final EmailCampaignWritePlatformService emailCampaignWritePlatformService;
@Autowired
public CloseEmailCampaignCommandHandler(final EmailCampaignWritePlatformService emailCampaignWritePlatformService) {
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
}
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.emailCampaignWritePlatformService.closeEmailCampaign(command.entityId(), command);
}
}
@@ -0,0 +1,46 @@
/**
* 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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL_CAMPAIGN", action = "CREATE")
public class CreateEmailCampaignCommandHandler implements NewCommandSourceHandler {
private EmailCampaignWritePlatformService emailCampaignWritePlatformService;
@Autowired
public CreateEmailCampaignCommandHandler(final EmailCampaignWritePlatformService emailCampaignWritePlatformService) {
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.emailCampaignWritePlatformService.create(command);
}
}
@@ -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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL", action = "CREATE")
public class CreateEmailCommandHandler implements NewCommandSourceHandler {
private final EmailWritePlatformService writePlatformService;
@Autowired
public CreateEmailCommandHandler(final EmailWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.create(command);
}
}
@@ -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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL_CAMPAIGN", action = "DELETE")
public class DeleteEmailCampaignCommandHandler implements NewCommandSourceHandler {
private final EmailCampaignWritePlatformService emailCampaignWritePlatformService;
@Autowired
public DeleteEmailCampaignCommandHandler(EmailCampaignWritePlatformService emailCampaignWritePlatformService) {
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.emailCampaignWritePlatformService.delete(command.entityId());
}
}
@@ -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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL", action = "DELETE")
public class DeleteEmailCommandHandler implements NewCommandSourceHandler {
private final EmailWritePlatformService writePlatformService;
@Autowired
public DeleteEmailCommandHandler(final EmailWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.delete(command.entityId());
}
}
@@ -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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL_CAMPAIGN", action = "REACTIVATE")
public class ReactivateEmailCampaignCommandHandler implements NewCommandSourceHandler {
private final EmailCampaignWritePlatformService emailCampaignWritePlatformService;
@Autowired
public ReactivateEmailCampaignCommandHandler(EmailCampaignWritePlatformService emailCampaignWritePlatformService) {
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.emailCampaignWritePlatformService.reactivateEmailCampaign(command.entityId(),command);
}
}
@@ -0,0 +1,44 @@
/**
* 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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL_CAMPAIGN", action = "UPDATE")
public class UpdateEmailCampaignCommandHandler implements NewCommandSourceHandler {
private final EmailCampaignWritePlatformService emailCampaignWritePlatformService;
@Autowired
public UpdateEmailCampaignCommandHandler(EmailCampaignWritePlatformService emailCampaignWritePlatformService) {
this.emailCampaignWritePlatformService = emailCampaignWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.emailCampaignWritePlatformService.update(command.entityId(),command);
}
}
@@ -0,0 +1,48 @@
/**
* 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.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailConfigurationWritePlatformService;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "EMAIL_CONFIGURATION", action = "UPDATE")
public class UpdateEmailConfigurationCommandHandler implements NewCommandSourceHandler {
private final EmailConfigurationWritePlatformService writePlatformService;
@Autowired
public UpdateEmailConfigurationCommandHandler(final EmailConfigurationWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.update(command);
}
}
@@ -0,0 +1,38 @@
/**
* 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.EmailBusinessRulesData;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignData;
import java.util.Collection;
public interface EmailCampaignReadPlatformService {
Collection<EmailBusinessRulesData> retrieveAll();
EmailBusinessRulesData retrieveOneTemplate(Long resourceId);
EmailCampaignData retrieveOne(Long resourceId);
Collection<EmailCampaignData> retrieveAllCampaign();
Collection<EmailCampaignData> retrieveAllScheduleActiveCampaign();
}
@@ -0,0 +1,285 @@
/**
* 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.DateTime;
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.RoutingDataSource;
import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
import org.apache.fineract.infrastructure.campaigns.email.data.ScheduledEmailEnumerations;
import org.apache.fineract.infrastructure.campaigns.email.exception.EmailBusinessRuleNotFound;
import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignNotFound;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailBusinessRulesData;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignData;
import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignTimeLine;
import org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignStatus;
import org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignStatusEnumerations;
import org.apache.fineract.infrastructure.campaigns.email.domain.EmailCampaignType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
@Service
public class EmailCampaignReadPlatformServiceImpl implements EmailCampaignReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final BusinessRuleMapper businessRuleMapper;
private final EmailCampaignMapper emailCampaignMapper;
@Autowired
public EmailCampaignReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.businessRuleMapper = new BusinessRuleMapper();
this.emailCampaignMapper = new EmailCampaignMapper();
}
private static final class EmailCampaignMapper implements RowMapper<EmailCampaignData>{
final String schema;
private EmailCampaignMapper() {
final StringBuilder sql = new StringBuilder(400);
sql.append("ec.id as id, ");
sql.append("ec.campaign_name as campaignName, ");
sql.append("ec.campaign_type as campaignType, ");
sql.append("ec.businessRule_id as businessRuleId, ");
sql.append("ec.email_subject as emailSubject, ");
sql.append("ec.email_message as emailMessage, ");
sql.append("ec.email_attachment_file_format as emailAttachmentFileFormat, ");
sql.append("sr.id as stretchyReportId, ");
sql.append("sr.report_name as reportName, sr.report_type as reportType, sr.report_subtype as reportSubType, ");
sql.append("sr.report_category as reportCategory, sr.report_sql as reportSql, sr.description as reportDescription, ");
sql.append("sr.core_report as coreReport, sr.use_report as useReport, ");
sql.append("ec.stretchy_report_param_map as stretchyReportParamMap, ");
sql.append("ec.param_value as paramValue, ");
sql.append("ec.status_enum as statusEnum, ");
sql.append("ec.recurrence as recurrence, ");
sql.append("ec.recurrence_start_date as recurrenceStartDate, ");
sql.append("ec.next_trigger_date as nextTriggerDate, ");
sql.append("ec.last_trigger_date as lastTriggerDate, ");
sql.append("ec.submittedon_date as submittedOnDate, ");
sql.append("sbu.username as submittedByUsername, ");
sql.append("ec.closedon_date as closedOnDate, ");
sql.append("clu.username as closedByUsername, ");
sql.append("acu.username as activatedByUsername, ");
sql.append("ec.approvedon_date as activatedOnDate ");
sql.append("from scheduled_email_campaign ec ");
sql.append("left join m_appuser sbu on sbu.id = ec.submittedon_userid ");
sql.append("left join m_appuser acu on acu.id = ec.approvedon_userid ");
sql.append("left join m_appuser clu on clu.id = ec.closedon_userid ");
sql.append("left join stretchy_report sr on ec.stretchy_report_id = sr.id");
this.schema = sql.toString();
}
public String schema() {
return this.schema;
}
@Override
public EmailCampaignData mapRow(ResultSet rs, int rowNum) throws SQLException {
final Long id = JdbcSupport.getLong(rs, "id");
final String campaignName = rs.getString("campaignName");
final Integer campaignType = JdbcSupport.getInteger(rs, "campaignType");
final Long businessRuleId = JdbcSupport.getLong(rs, "businessRuleId");
final String paramValue = rs.getString("paramValue");
final String emailSubject = rs.getString("emailSubject");
final String emailMessage = rs.getString("emailMessage");
final String emailAttachmentFileFormatString = rs.getString("emailAttachmentFileFormat");
final String stretchyReportParamMap = rs.getString("stretchyReportParamMap");
EnumOptionData emailAttachmentFileFormat = null;
if (emailAttachmentFileFormatString != null) {
emailAttachmentFileFormat = ScheduledEmailEnumerations.emailAttachementFileFormat(emailAttachmentFileFormatString);
}
final Long reportId = JdbcSupport.getLong(rs, "stretchyReportId");
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);
final Integer statusId = JdbcSupport.getInteger(rs, "statusEnum");
final EnumOptionData status = EmailCampaignStatusEnumerations.status(statusId);
final DateTime nextTriggerDate = JdbcSupport.getDateTime(rs, "nextTriggerDate");
final LocalDate lastTriggerDate = JdbcSupport.getLocalDate(rs, "lastTriggerDate");
final LocalDate closedOnDate = JdbcSupport.getLocalDate(rs, "closedOnDate");
final String closedByUsername = rs.getString("closedByUsername");
final LocalDate submittedOnDate = JdbcSupport.getLocalDate(rs, "submittedOnDate");
final String submittedByUsername = rs.getString("submittedByUsername");
final LocalDate activatedOnDate = JdbcSupport.getLocalDate(rs, "activatedOnDate");
final String activatedByUsername = rs.getString("activatedByUsername");
final String recurrence =rs.getString("recurrence");
final DateTime recurrenceStartDate = JdbcSupport.getDateTime(rs, "recurrenceStartDate");
final EmailCampaignTimeLine emailCampaignTimeLine = new EmailCampaignTimeLine(submittedOnDate,submittedByUsername,
activatedOnDate,activatedByUsername,closedOnDate,closedByUsername);
return EmailCampaignData.instance(id,campaignName,campaignType,businessRuleId,paramValue,status,emailSubject,emailMessage,
emailAttachmentFileFormatString,reportId,stretchyReportParamMap,nextTriggerDate,lastTriggerDate,emailCampaignTimeLine,
recurrenceStartDate,recurrence);
}
}
private static final class BusinessRuleMapper implements ResultSetExtractor<List<EmailBusinessRulesData>>{
final String schema;
private BusinessRuleMapper() {
final StringBuilder sql = new StringBuilder(300);
sql.append("sr.id as id, ");
sql.append("sr.report_name as reportName, ");
sql.append("sr.report_type as reportType, ");
sql.append("sr.report_subtype as reportSubType, ");
sql.append("sr.description as description, ");
sql.append("sp.parameter_variable as params, ");
sql.append("sp.parameter_FormatType as paramType, ");
sql.append("sp.parameter_label as paramLabel, ");
sql.append("sp.parameter_name as paramName ");
sql.append("from stretchy_report sr ");
sql.append("left join stretchy_report_parameter as srp on srp.report_id = sr.id ");
sql.append("left join stretchy_parameter as sp on sp.id = srp.parameter_id ");
this.schema = sql.toString();
}
public String schema(){
return this.schema;
}
@Override
public List<EmailBusinessRulesData> extractData(ResultSet rs) throws SQLException, DataAccessException {
List<EmailBusinessRulesData> emailBusinessRulesDataList = new ArrayList<EmailBusinessRulesData>();
EmailBusinessRulesData emailBusinessRulesData = null;
Map<Long,EmailBusinessRulesData> mapOfSameObjects = new HashMap<Long, EmailBusinessRulesData>();
while(rs.next()){
final Long id = rs.getLong("id");
emailBusinessRulesData = mapOfSameObjects.get(id);
if(emailBusinessRulesData == null){
final String reportName = rs.getString("reportName") ;
final String reportType = rs.getString("reportType");
final String reportSubType = rs.getString("reportSubType");
final String paramName = rs.getString("paramName");
final String paramLabel = rs.getString("paramLabel");
final String description = rs.getString("description");
Map<String,Object> hashMap = new HashMap<String, Object>();
hashMap.put(paramLabel,paramName);
emailBusinessRulesData = EmailBusinessRulesData.instance(id,reportName,reportType,hashMap,reportSubType,description);
mapOfSameObjects.put(id,emailBusinessRulesData);
//add to the list
emailBusinessRulesDataList.add(emailBusinessRulesData);
}
//add new paramType to the existing object
Map<String,Object> hashMap = new HashMap<String, Object>();
final String paramName = rs.getString("paramName");
final String paramLabel = rs.getString("paramLabel");
hashMap.put(paramLabel,paramName);
//get existing map and add new items to it
emailBusinessRulesData.getReportParamName().putAll(hashMap);
}
return emailBusinessRulesDataList;
}
}
@Override
public Collection<EmailBusinessRulesData> retrieveAll() {
final String searchType = "Email";
final String sql = "select " + this.businessRuleMapper.schema() + " where sr.report_type = ?";
return this.jdbcTemplate.query(sql, this.businessRuleMapper, searchType);
}
@Override
public EmailBusinessRulesData retrieveOneTemplate(Long resourceId) {
final String searchType = "Email";
final String sql = "select " + this.businessRuleMapper.schema() + " where sr.report_type = ? and sr.id = ?";
List<EmailBusinessRulesData> retrieveOne = this.jdbcTemplate.query(sql, this.businessRuleMapper, searchType,resourceId);
try{
EmailBusinessRulesData emailBusinessRulesData = retrieveOne.get(0);
return emailBusinessRulesData;
}
catch (final IndexOutOfBoundsException e){
throw new EmailBusinessRuleNotFound(resourceId);
}
}
@Override
public EmailCampaignData retrieveOne(Long resourceId) {
final Integer isVisible =1;
try{
final String sql = "select " + this.emailCampaignMapper.schema + " where ec.id = ? and ec.is_visible = ?";
return this.jdbcTemplate.queryForObject(sql, this.emailCampaignMapper, resourceId,isVisible);
} catch (final EmptyResultDataAccessException e) {
throw new EmailCampaignNotFound(resourceId);
}
}
@Override
public Collection<EmailCampaignData> retrieveAllCampaign() {
final Integer visible = 1;
final String sql = "select " + this.emailCampaignMapper.schema() + " where ec.is_visible = ?";
return this.jdbcTemplate.query(sql, this.emailCampaignMapper, visible);
}
@Override
public Collection<EmailCampaignData> retrieveAllScheduleActiveCampaign() {
final Integer scheduleCampaignType = EmailCampaignType.SCHEDULE.getValue();
final Integer statusEnum = EmailCampaignStatus.ACTIVE.getValue();
final Integer visible = 1;
final String sql = "select " + this.emailCampaignMapper.schema() + " where ec.status_enum = ? and ec.campaign_type = ? and ec.is_visible = ?";
return this.jdbcTemplate.query(sql,this.emailCampaignMapper, statusEnum,scheduleCampaignType,visible);
}
}
@@ -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;
}
}
@@ -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;
}
@@ -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);
}
@@ -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);
}
}
}
@@ -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);
}
@@ -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();
}
}
@@ -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);
}
@@ -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;
}
}
@@ -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);
}
@@ -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);
}
}
@@ -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);
}
@@ -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());
}
}
@@ -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);
@@ -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 {
@@ -225,6 +225,21 @@ public final class SearchParameters {
staffId, accountNo, loanId, savingsId, orphansOnly, isSelfUser);
}
public static SearchParameters forEmailCampaign(final String sqlSearch, final Integer offset, final Integer limit, final String orderBy, final String sortOrder) {
final String externalId = null;
final Integer maxLimitAllowed = getCheckedLimit(limit);
final Long staffId = null;
final String accountNo = null;
final Long loanId = null;
final Long savingsId = null;
final Boolean orphansOnly = false;
final boolean isSelfUser = false;
return new SearchParameters(sqlSearch, null, externalId, null, null, null, null, offset, maxLimitAllowed, orderBy, sortOrder,
staffId, accountNo, loanId, savingsId, orphansOnly, isSelfUser);
}
private SearchParameters(final String sqlSearch, final Long officeId, final String externalId, final String name,
final String hierarchy, final String firstname, final String lastname, final Integer offset, final Integer limit,
final String orderBy, final String sortOrder, final Long staffId, final String accountNo, final Long loanId,
@@ -276,4 +276,8 @@ public final class Report extends AbstractPersistableCustom<Long> {
return false;
}
public Set<ReportParameterUsage> getReportParameterUsages() {
return this.reportParameterUsages;
}
}
@@ -86,4 +86,8 @@ public final class ReportParameterUsage extends AbstractPersistableCustom<Long>
public void updateParameterName(final String parameterName) {
this.reportParameterName = parameterName;
}
public String getReportParameterName() {
return this.reportParameterName;
}
}
@@ -18,10 +18,14 @@
*/
package org.apache.fineract.infrastructure.dataqueries.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ReportParameterUsageRepository extends JpaRepository<ReportParameterUsage, Long>,
JpaSpecificationExecutor<ReportParameterUsage> {
// no added behaviour
List<ReportParameterUsage> findByReport(Report report);
}
@@ -18,7 +18,9 @@
*/
package org.apache.fineract.infrastructure.dataqueries.service;
import java.io.ByteArrayOutputStream;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import javax.ws.rs.core.StreamingOutput;
@@ -26,6 +28,7 @@ import javax.ws.rs.core.StreamingOutput;
import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
import org.apache.fineract.infrastructure.dataqueries.data.ReportData;
import org.apache.fineract.infrastructure.dataqueries.data.ReportParameterData;
import org.apache.fineract.useradministration.domain.AppUser;
public interface ReadReportingService {
@@ -45,8 +48,11 @@ public interface ReadReportingService {
Collection<String> getAllowedReportTypes();
//needed for smsCampaign jobs where securityContext is null
GenericResultsetData retrieveGenericResultSetForSmsCampaign(String name, String type, Map<String, String> extractedQueryParams);
String sqlToRunForSmsCampaign(String name, String type, Map<String, String> queryParams);
//needed for smsCampaign and emailCampaign jobs where securityContext is null
GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String name, String type, Map<String, String> extractedQueryParams);
String sqlToRunForSmsEmailCampaign(String name, String type, Map<String, String> queryParams);
ByteArrayOutputStream generatePentahoReportAsOutputStream(String reportName, String outputTypeParam,
Map<String, String> queryParams, Locale locale, AppUser runReportAsUser, StringBuilder errorLog);
}
@@ -28,6 +28,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -48,6 +49,16 @@ import org.apache.fineract.infrastructure.documentmanagement.contentrepository.F
import org.apache.fineract.infrastructure.report.provider.ReportingProcessServiceProvider;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.useradministration.domain.AppUser;
import org.pentaho.reporting.engine.classic.core.DefaultReportEnvironment;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.modules.output.pageable.pdf.PdfReportUtil;
import org.pentaho.reporting.engine.classic.core.modules.output.table.csv.CSVReportUtil;
import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlReportUtil;
import org.pentaho.reporting.engine.classic.core.modules.output.table.xls.ExcelReportUtil;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -486,11 +497,11 @@ public class ReadReportingServiceImpl implements ReadReportingService {
}
@Override
public GenericResultsetData retrieveGenericResultSetForSmsCampaign(String name, String type, Map<String, String> queryParams) {
public GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String name, String type, Map<String, String> queryParams) {
final long startTime = System.currentTimeMillis();
logger.info("STARTING REPORT: " + name + " Type: " + type);
final String sql = sqlToRunForSmsCampaign(name, type, queryParams);
final String sql = sqlToRunForSmsEmailCampaign(name, type, queryParams);
final GenericResultsetData result = this.genericDataService.fillGenericResultSet(sql);
@@ -500,7 +511,7 @@ public class ReadReportingServiceImpl implements ReadReportingService {
}
@Override
public String sqlToRunForSmsCampaign(final String name, final String type, final Map<String, String> queryParams) {
public String sqlToRunForSmsEmailCampaign(final String name, final String type, final Map<String, String> queryParams) {
String sql = getSql(name, type);
final Set<String> keys = queryParams.keySet();
@@ -515,4 +526,81 @@ public class ReadReportingServiceImpl implements ReadReportingService {
return sql;
}
@Override
public ByteArrayOutputStream generatePentahoReportAsOutputStream(final String reportName, final String outputTypeParam, final Map<String, String> queryParams,
final Locale locale, final AppUser runReportAsUser, final StringBuilder errorLog) {
String outputType = "HTML";
if (StringUtils.isNotBlank(outputTypeParam)) {
outputType = outputTypeParam;
}
if (!(outputType.equalsIgnoreCase("HTML") || outputType.equalsIgnoreCase("PDF") || outputType.equalsIgnoreCase("XLS") || outputType
.equalsIgnoreCase("CSV"))) { throw new PlatformDataIntegrityException("error.msg.invalid.outputType",
"No matching Output Type: " + outputType); }
if (this.noPentaho) { throw new PlatformDataIntegrityException("error.msg.no.pentaho", "Pentaho is not enabled",
"Pentaho is not enabled"); }
final String reportPath = FileSystemContentRepository.FINERACT_BASE_DIR + File.separator + "pentahoReports" + File.separator
+ reportName + ".prpt";
logger.info("Report path: " + reportPath);
// load report definition
final ResourceManager manager = new ResourceManager();
manager.registerDefaults();
Resource res;
try {
res = manager.createDirectly(reportPath, MasterReport.class);
final MasterReport masterReport = (MasterReport) res.getResource();
final DefaultReportEnvironment reportEnvironment = (DefaultReportEnvironment) masterReport.getReportEnvironment();
if (locale != null) {
reportEnvironment.setLocale(locale);
}
addParametersToReport(masterReport, queryParams, runReportAsUser, errorLog);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
if ("PDF".equalsIgnoreCase(outputType)) {
PdfReportUtil.createPDF(masterReport, baos);
return baos;
}
if ("XLS".equalsIgnoreCase(outputType)) {
ExcelReportUtil.createXLS(masterReport, baos);
return baos;
}
if ("CSV".equalsIgnoreCase(outputType)) {
CSVReportUtil.createCSV(masterReport, baos, "UTF-8");
return baos;
}
if ("HTML".equalsIgnoreCase(outputType)) {
HtmlReportUtil.createStreamHTML(masterReport, baos);
return baos;
}
} catch (final ResourceException e) {
errorLog.append("ReadReportingServiceImpl.generatePentahoReportAsOutputStream method threw a Pentaho ResourceException "
+ "exception: " + e.getMessage() + " ---------- ");
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
} catch (final ReportProcessingException e) {
errorLog.append("ReadReportingServiceImpl.generatePentahoReportAsOutputStream method threw a Pentaho ReportProcessingException "
+ "exception: " + e.getMessage() + " ---------- ");
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
} catch (final IOException e) {
errorLog.append("ReadReportingServiceImpl.generatePentahoReportAsOutputStream method threw an IOException "
+ "exception: " + e.getMessage() + " ---------- ");
throw new PlatformDataIntegrityException("error.msg.reporting.error", e.getMessage());
}
errorLog.append("ReadReportingServiceImpl.generatePentahoReportAsOutputStream method threw a PlatformDataIntegrityException "
+ "exception: No matching Output Type: " + outputType + " ---------- ");
throw new PlatformDataIntegrityException("error.msg.invalid.outputType", "No matching Output Type: " + outputType);
}
}
@@ -46,7 +46,11 @@ public enum JobName {
UPDATE_SMS_OUTBOUND_WITH_CAMPAIGN_MESSAGE("Update SMS Outbound with Campaign Message"),
SEND_MESSAGES_TO_SMS_GATEWAY("Send Messages to SMS Gateway"),
GET_DELIVERY_REPORTS_FROM_SMS_GATEWAY("Get Delivery Reports from SMS Gateway"),
GENERATE_ADHOCCLIENT_SCEHDULE("Generate AdhocClient Schedule");
GENERATE_ADHOCCLIENT_SCEHDULE("Generate AdhocClient Schedule"),
SEND_MESSAGES_TO_EMAIL_GATEWAY("Send messages to Email gateway"),
UPDATE_EMAIL_OUTBOUND_WITH_CAMPAIGN_MESSAGE("Update Email Outbound with campaign message"),
EXECUTE_EMAIL("Execute Email");
private final String name;
private JobName(final String name) {
@@ -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.reportmailingjob.domain;
public enum ReportMailingJobEmailAttachmentFileFormat {
INVALID(0, "ReportMailingJobEmailAttachmentFileFormat.invalid", "invalid"),
XLS(1, "ReportMailingJobEmailAttachmentFileFormat.xls", "xls"),
PDF(2, "ReportMailingJobEmailAttachmentFileFormat.pdf", "pdf"),
CSV(3, "ReportMailingJobEmailAttachmentFileFormat.csv", "csv");
private String code;
private String value;
private Integer id;
private ReportMailingJobEmailAttachmentFileFormat(final Integer id, final String code, final String value) {
this.value = value;
this.code = code;
this.id = id;
}
public static ReportMailingJobEmailAttachmentFileFormat instance(final String value) {
ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat = INVALID;
switch (value) {
case "xls":
emailAttachmentFileFormat = XLS;
break;
case "pdf":
emailAttachmentFileFormat = PDF;
break;
case "csv":
emailAttachmentFileFormat = CSV;
break;
default:
break;
}
return emailAttachmentFileFormat;
}
public static ReportMailingJobEmailAttachmentFileFormat instance(final Integer id) {
ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat = INVALID;
switch (id) {
case 1:
emailAttachmentFileFormat = XLS;
break;
case 2:
emailAttachmentFileFormat = PDF;
break;
case 3:
emailAttachmentFileFormat = CSV;
break;
default:
break;
}
return emailAttachmentFileFormat;
}
/**
* @return the code
*/
public String getCode() {
return code;
}
/**
* @return the value
*/
public String getValue() {
return value;
}
/**
* @return the id
*/
public Integer getId() {
return id;
}
/**
* @return list of valid ReportMailingJobEmailAttachmentFileFormat ids
**/
public static Object[] validValues() {
return new Object[] { XLS.getId(), PDF.getId(), CSV.getId() };
}
}
@@ -0,0 +1,143 @@
/**
* 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.helper;
import java.net.InetAddress;
import org.apache.commons.lang.StringUtils;
/**
* This utility provides methods to either convert an IPv4 address to its long format or a 32bit dotted format.
*
* @see http://hawkee.com/snippet/9731/
*/
public class IPv4Helper {
/**
* Returns the long format of the provided IP address.
*
* @param ipAddress the IP address
* @return the long format of <code>ipAddress</code>
* @throws IllegalArgumentException if <code>ipAddress</code> is invalid
*/
public static long ipAddressToLong(String ipAddress) {
if (ipAddress == null || ipAddress.isEmpty()) {
throw new IllegalArgumentException("ip address cannot be null or empty");
}
String[] octets = ipAddress.split(java.util.regex.Pattern.quote("."));
if (octets.length != 4) {
throw new IllegalArgumentException("invalid ip address");
}
long ip = 0;
for (int i = 3; i >= 0; i--) {
long octet = Long.parseLong(octets[3 - i]);
if (octet > 255 || octet < 0) {
throw new IllegalArgumentException("invalid ip address");
}
ip |= octet << (i * 8);
}
return ip;
}
/**
* Returns the 32bit dotted format of the provided long ip.
*
* @param ip the long ip
* @return the 32bit dotted format of <code>ip</code>
* @throws IllegalArgumentException if <code>ip</code> is invalid
*/
public static String longToIpAddress(long ip) {
// if ip is bigger than 255.255.255.255 or smaller than 0.0.0.0
if (ip > 4294967295l || ip < 0) {
throw new IllegalArgumentException("invalid ip");
}
StringBuilder ipAddress = new StringBuilder();
for (int i = 3; i >= 0; i--) {
int shift = i * 8;
ipAddress.append((ip & (0xff << shift)) >> shift);
if (i > 0) {
ipAddress.append(".");
}
}
return ipAddress.toString();
}
/**
* check if an IP Address is within a given range of IP Addresses
*
* @param ipAddress -- the IP Address to be checked
* @param startOfRange -- the first IP address in the range
* @param endOfRange -- the last IP address in the range
* @return boolean true if IP address is in the range of IP addresses
**/
public static boolean ipAddressIsInRange(final String ipAddress, final String startOfRange, final String endOfRange) {
final long ipAddressToLong = ipAddressToLong(ipAddress);
final long startOfRangeToLong = ipAddressToLong(startOfRange);
final long endOfRangeToLong = ipAddressToLong(endOfRange);
long diff = ipAddressToLong - startOfRangeToLong;
return (diff >= 0 && (diff <= (endOfRangeToLong - startOfRangeToLong)));
}
/**
* check if the java application is running on a local machine
*
* @return true if the application is running on a local machine else false
**/
public static boolean applicationIsRunningOnLocalMachine() {
boolean isRunningOnLocalMachine = false;
try {
final InetAddress localHost = InetAddress.getLocalHost();
if (localHost != null) {
final String hostAddress = localHost.getHostAddress();
final String startOfIpAddressRange = "127.0.0.0";
final String endOfIpAddressRange = "127.255.255.255";
if (StringUtils.isNotEmpty(hostAddress)) {
isRunningOnLocalMachine = ipAddressIsInRange(hostAddress, startOfIpAddressRange, endOfIpAddressRange);
}
}
}
catch (Exception exception) { }
return isRunningOnLocalMachine;
}
/**
* check if the java application is not running on a local machine
*
* @return true if the application is not running on a local machine else false
**/
public static boolean applicationIsNotRunningOnLocalMachine() {
return !applicationIsRunningOnLocalMachine();
}
}
@@ -61,6 +61,9 @@ public class Staff extends AbstractPersistableCustom<Long> {
@Column(name = "external_id", length = 100, nullable = true, unique = true)
private String externalId;
@Column(name = "email_address", length = 50, unique = true)
private String emailAddress;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@@ -246,6 +249,10 @@ public class Staff extends AbstractPersistableCustom<Long> {
return getId().equals(staff.getId());
}
public String emailAddress() {
return emailAddress;
}
public Long officeId() {
return this.office.getId();
}
@@ -83,6 +83,7 @@ public class ClientApiConstants {
public static final String accountNoParamName = "accountNo";
public static final String externalIdParamName = "externalId";
public static final String mobileNoParamName = "mobileNo";
public static final String emailAddressParamName = "emailAddress";
public static final String firstnameParamName = "firstname";
public static final String middlenameParamName = "middlename";
public static final String lastnameParamName = "lastname";
@@ -194,7 +195,7 @@ public class ClientApiConstants {
protected static final Set<String> CLIENT_RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(idParamName,
accountNoParamName,
externalIdParamName, statusParamName, activeParamName, activationDateParamName, firstnameParamName, middlenameParamName,
lastnameParamName, fullnameParamName, displayNameParamName, mobileNoParamName, officeIdParamName, officeNameParamName,
lastnameParamName, fullnameParamName, displayNameParamName, mobileNoParamName, emailAddressParamName, officeIdParamName, officeNameParamName,
transferToOfficeIdParamName, transferToOfficeNameParamName, hierarchyParamName, imageIdParamName, imagePresentParamName,
staffIdParamName, staffNameParamName, timelineParamName, groupsParamName, officeOptionsParamName, staffOptionsParamName,
dateOfBirthParamName, genderParamName, clientTypeParamName, clientClassificationParamName, legalFormParamName,
@@ -29,7 +29,7 @@ public class ClientApiCollectionConstants extends ClientApiConstants{
protected static final Set<String> CLIENT_CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(familyMembers,address,localeParamName, dateFormatParamName, groupIdParamName, accountNoParamName, externalIdParamName,
mobileNoParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName,
mobileNoParamName, emailAddressParamName, firstnameParamName, middlenameParamName, lastnameParamName, fullnameParamName, officeIdParamName,
activeParamName, activationDateParamName, staffIdParamName, submittedOnDateParamName, savingsProductIdParamName,
dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName,
clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, datatables, isStaffParamName));
@@ -39,7 +39,7 @@ public class ClientApiCollectionConstants extends ClientApiConstants{
constitutionIdParamName, mainBusinessLineIdParamName, datatables));
protected static final Set<String> CLIENT_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, firstnameParamName, middlenameParamName,
dateFormatParamName, accountNoParamName, externalIdParamName, mobileNoParamName, emailAddressParamName, firstnameParamName, middlenameParamName,
lastnameParamName, fullnameParamName, activeParamName, activationDateParamName, staffIdParamName, savingsProductIdParamName,
dateOfBirthParamName, genderIdParamName, clientTypeIdParamName, clientClassificationIdParamName, submittedOnDateParamName, clientNonPersonDetailsParamName, displaynameParamName, legalFormIdParamName, isStaffParamName));
@@ -58,6 +58,7 @@ final public class ClientData implements Comparable<ClientData> {
private final String fullname;
private final String displayName;
private final String mobileNo;
private final String emailAddress;
private final LocalDate dateOfBirth;
private final CodeValueData gender;
private final CodeValueData clientType;
@@ -126,6 +127,7 @@ final public class ClientData implements Comparable<ClientData> {
final String displayName = null;
final String externalId = null;
final String mobileNo = null;
final String emailAddress = null;
final LocalDate dateOfBirth = null;
final CodeValueData gender = null;
final Long imageId = null;
@@ -143,7 +145,7 @@ final public class ClientData implements Comparable<ClientData> {
final Boolean isStaff = false;
final ClientNonPersonData clientNonPersonDetails = null;
return new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname,
middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, joinedDate, imageId, staffId,
middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, joinedDate, imageId, staffId,
staffName, officeOptions, groups, staffOptions, narrations, genderOptions, timeline, savingProductOptions,
savingsProductId, savingsProductName, savingsAccountId, savingAccountOptions, clientType, clientClassification,
clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions,
@@ -155,7 +157,7 @@ final public class ClientData implements Comparable<ClientData> {
return new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, clientData.officeName,
clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.emailAddress,
clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, clientData.staffId,
clientData.staffName, templateData.officeOptions, clientData.groups, templateData.staffOptions, templateData.narrations,
templateData.genderOptions, clientData.timeline, templateData.savingProductOptions, clientData.savingsProductId,
@@ -171,7 +173,7 @@ final public class ClientData implements Comparable<ClientData> {
return new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, clientData.officeName,
clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.emailAddress,
clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, clientData.staffId,
clientData.staffName, clientData.officeOptions, clientData.groups, clientData.staffOptions, clientData.narrations,
clientData.genderOptions, clientData.timeline, clientData.savingProductOptions, clientData.savingsProductId,
@@ -185,7 +187,7 @@ final public class ClientData implements Comparable<ClientData> {
public static ClientData setParentGroups(final ClientData clientData, final Collection<GroupGeneralData> parentGroups) {
return new ClientData(clientData.accountNo, clientData.status, clientData.subStatus, clientData.officeId, clientData.officeName,
clientData.transferToOfficeId, clientData.transferToOfficeName, clientData.id, clientData.firstname, clientData.middlename,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo,
clientData.lastname, clientData.fullname, clientData.displayName, clientData.externalId, clientData.mobileNo, clientData.emailAddress,
clientData.dateOfBirth, clientData.gender, clientData.activationDate, clientData.imageId, clientData.staffId,
clientData.staffName, clientData.officeOptions, parentGroups, clientData.staffOptions, null, null, clientData.timeline,
clientData.savingProductOptions, clientData.savingsProductId, clientData.savingsProductName, clientData.savingsAccountId,
@@ -203,6 +205,7 @@ final public class ClientData implements Comparable<ClientData> {
final String transferToOfficeName = null;
final String externalId = null;
final String mobileNo = null;
final String emailAddress = null;
final LocalDate dateOfBirth = null;
final CodeValueData gender = null;
final LocalDate activationDate = null;
@@ -234,7 +237,7 @@ final public class ClientData implements Comparable<ClientData> {
final Boolean isStaff = false;
final ClientNonPersonData clientNonPerson = null;
return new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname,
middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate, imageId, staffId,
middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, activationDate, imageId, staffId,
staffName, allowedOffices, groups, staffOptions, closureReasons, genderOptions, timeline, savingProductOptions,
savingsProductId, savingsProductName, savingsAccountId, savingAccountOptions, clientType, clientClassification,
clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions,
@@ -253,6 +256,7 @@ final public class ClientData implements Comparable<ClientData> {
final String fullname = null;
final String externalId = null;
final String mobileNo = null;
final String emailAddress = null;
final LocalDate dateOfBirth = null;
final CodeValueData gender = null;
final LocalDate activationDate = null;
@@ -282,7 +286,7 @@ final public class ClientData implements Comparable<ClientData> {
final Boolean isStaff = false;
final ClientNonPersonData clientNonPerson = null;
return new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname,
middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate, imageId, staffId,
middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, activationDate, imageId, staffId,
staffName, allowedOffices, groups, staffOptions, closureReasons, genderOptions, timeline, savingProductOptions,
savingsProductId, savingsProductName, savingsAccountId, savingAccountOptions, clientType, clientClassification,
clientTypeOptions, clientClassificationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions,
@@ -293,7 +297,7 @@ final public class ClientData implements Comparable<ClientData> {
public static ClientData instance(final String accountNo, final EnumOptionData status, final CodeValueData subStatus,
final Long officeId, final String officeName, final Long transferToOfficeId, final String transferToOfficeName, final Long id,
final String firstname, final String middlename, final String lastname, final String fullname, final String displayName,
final String externalId, final String mobileNo, final LocalDate dateOfBirth, final CodeValueData gender,
final String externalId, final String mobileNo, final String emailAddress, final LocalDate dateOfBirth, final CodeValueData gender,
final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName,
final ClientTimelineData timeline, final Long savingsProductId, final String savingsProductName, final Long savingsAccountId,
final CodeValueData clientType, final CodeValueData clientClassification, final EnumOptionData legalForm, final ClientNonPersonData clientNonPerson, final Boolean isStaff) {
@@ -311,7 +315,7 @@ final public class ClientData implements Comparable<ClientData> {
final List<EnumOptionData> clientLegalFormOptions = null;
final ClientFamilyMembersData familyMemberOptions=null;
return new ClientData(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id, firstname,
middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate, imageId, staffId,
middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, activationDate, imageId, staffId,
staffName, allowedOffices, groups, staffOptions, closureReasons, genderOptions, timeline, savingProductOptions,
savingsProductId, savingsProductName, savingsAccountId, null, clientType, clientClassification, clientTypeOptions,
clientClassificationOptions, clientNonPersonConstitutionOptions, clientNonPersonMainBusinessLineOptions, clientNonPerson,
@@ -322,7 +326,7 @@ final public class ClientData implements Comparable<ClientData> {
private ClientData(final String accountNo, final EnumOptionData status, final CodeValueData subStatus, final Long officeId,
final String officeName, final Long transferToOfficeId, final String transferToOfficeName, final Long id,
final String firstname, final String middlename, final String lastname, final String fullname, final String displayName,
final String externalId, final String mobileNo, final LocalDate dateOfBirth, final CodeValueData gender,
final String externalId, final String mobileNo, final String emailAddress, final LocalDate dateOfBirth, final CodeValueData gender,
final LocalDate activationDate, final Long imageId, final Long staffId, final String staffName,
final Collection<OfficeData> allowedOffices, final Collection<GroupGeneralData> groups,
final Collection<StaffData> staffOptions, final Collection<CodeValueData> narrations,
@@ -354,6 +358,7 @@ final public class ClientData implements Comparable<ClientData> {
this.displayName = StringUtils.defaultIfEmpty(displayName, null);
this.externalId = StringUtils.defaultIfEmpty(externalId, null);
this.mobileNo = StringUtils.defaultIfEmpty(mobileNo, null);
this.emailAddress = StringUtils.defaultIfEmpty(emailAddress, null);
this.activationDate = activationDate;
this.dateOfBirth = dateOfBirth;
this.gender = gender;
@@ -440,6 +445,7 @@ final public class ClientData implements Comparable<ClientData> {
.append(this.id, obj.id) //
.append(this.displayName, obj.displayName) //
.append(this.mobileNo, obj.mobileNo) //
.append(this.emailAddress, obj.emailAddress) //
.toComparison();
}
@@ -453,6 +459,7 @@ final public class ClientData implements Comparable<ClientData> {
.append(this.id, rhs.id) //
.append(this.displayName, rhs.displayName) //
.append(this.mobileNo, rhs.mobileNo) //
.append(this.emailAddress, rhs.emailAddress) //
.isEquals();
}
@@ -114,6 +114,9 @@ public final class Client extends AbstractPersistableCustom<Long> {
@Column(name = "mobile_no", length = 50, nullable = false, unique = true)
private String mobileNo;
@Column(name = "email_address", length = 50, unique = true)
private String emailAddress;
@Column(name = "is_staff", nullable = false)
private boolean isStaff;
@@ -236,6 +239,7 @@ public final class Client extends AbstractPersistableCustom<Long> {
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
final String mobileNo = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
final String emailAddress = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
final String middlename = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
@@ -269,7 +273,7 @@ public final class Client extends AbstractPersistableCustom<Long> {
}
final Long savingsAccountId = null;
return new Client(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname,
activationDate, officeJoiningDate, externalId, mobileNo, staff, submittedOnDate, savingsProductId, savingsAccountId, dataOfBirth,
activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff, submittedOnDate, savingsProductId, savingsAccountId, dataOfBirth,
gender, clientType, clientClassification, legalForm, isStaff);
}
@@ -279,7 +283,7 @@ public final class Client extends AbstractPersistableCustom<Long> {
private Client(final AppUser currentUser, final ClientStatus status, final Office office, final Group clientParentGroup,
final String accountNo, final String firstname, final String middlename, final String lastname, final String fullname,
final LocalDate activationDate, final LocalDate officeJoiningDate, final String externalId, final String mobileNo,
final LocalDate activationDate, final LocalDate officeJoiningDate, final String externalId, final String mobileNo, final String emailAddress,
final Staff staff, final LocalDate submittedOnDate, final Long savingsProductId, final Long savingsAccountId,
final LocalDate dateOfBirth, final CodeValue gender, final CodeValue clientType, final CodeValue clientClassification, final Integer legalForm, final Boolean isStaff) {
@@ -307,6 +311,12 @@ public final class Client extends AbstractPersistableCustom<Long> {
this.mobileNo = null;
}
if (StringUtils.isNotBlank(emailAddress)) {
this.emailAddress = emailAddress.trim();
} else {
this.emailAddress = null;
}
if (activationDate != null) {
this.activationDate = activationDate.toDateTimeAtStartOfDay().toDate();
this.activatedBy = currentUser;
@@ -498,6 +508,12 @@ public final class Client extends AbstractPersistableCustom<Long> {
actualChanges.put(ClientApiConstants.mobileNoParamName, newValue);
this.mobileNo = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.emailAddressParamName, this.emailAddress)) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
actualChanges.put(ClientApiConstants.emailAddressParamName, newValue);
this.emailAddress = StringUtils.defaultIfEmpty(newValue, null);
}
if (command.isChangeInStringParameterNamed(ClientApiConstants.firstnameParamName, this.firstname)) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
@@ -771,6 +787,10 @@ public final class Client extends AbstractPersistableCustom<Long> {
return this.mobileNo;
}
public String emailAddress() {
return this.emailAddress;
}
public void setMobileNo(final String mobileNo) {
this.mobileNo = mobileNo;
}
@@ -787,6 +807,10 @@ public final class Client extends AbstractPersistableCustom<Long> {
return this.externalId;
}
public void setEmailAddress(final String emailAddress) {
this.emailAddress = emailAddress;
}
public String getDisplayName() {
return this.displayName;
}
@@ -363,6 +363,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
sqlBuilder.append("c.mobile_no as mobileNo, ");
sqlBuilder.append("c.is_staff as isStaff, ");
sqlBuilder.append("c.email_address as emailAddress, ");
sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
sqlBuilder.append("c.gender_cv_id as genderId, ");
sqlBuilder.append("cv.code_value as genderValue, ");
@@ -452,6 +453,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final String externalId = rs.getString("externalId");
final String mobileNo = rs.getString("mobileNo");
final boolean isStaff = rs.getBoolean("isStaff");
final String emailAddress = rs.getString("emailAddress");
final LocalDate dateOfBirth = JdbcSupport.getLocalDate(rs, "dateOfBirth");
final Long genderId = JdbcSupport.getLong(rs, "genderId");
final String genderValue = rs.getString("genderValue");
@@ -511,7 +513,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
closedByUsername, closedByFirstname, closedByLastname);
return ClientData.instance(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id,
firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate,
firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, activationDate,
imageId, staffId, staffName, timeline, savingsProductId, savingsProductName, savingsAccountId, clienttype,
classification, legalForm, clientNonPerson, isStaff);
@@ -547,6 +549,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
builder.append("c.fullname as fullname, c.display_name as displayName, ");
builder.append("c.mobile_no as mobileNo, ");
builder.append("c.is_staff as isStaff, ");
builder.append("c.email_address as emailAddress, ");
builder.append("c.date_of_birth as dateOfBirth, ");
builder.append("c.gender_cv_id as genderId, ");
builder.append("cv.code_value as genderValue, ");
@@ -635,6 +638,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
final String externalId = rs.getString("externalId");
final String mobileNo = rs.getString("mobileNo");
final boolean isStaff = rs.getBoolean("isStaff");
final String emailAddress = rs.getString("emailAddress");
final LocalDate dateOfBirth = JdbcSupport.getLocalDate(rs, "dateOfBirth");
final Long genderId = JdbcSupport.getLong(rs, "genderId");
final String genderValue = rs.getString("genderValue");
@@ -693,7 +697,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService
closedByUsername, closedByFirstname, closedByLastname);
return ClientData.instance(accountNo, status, subStatus, officeId, officeName, transferToOfficeId, transferToOfficeName, id,
firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, dateOfBirth, gender, activationDate,
firstname, middlename, lastname, fullname, displayName, externalId, mobileNo, emailAddress, dateOfBirth, gender, activationDate,
imageId, staffId, staffName, timeline, savingsProductId, savingsProductName, savingsAccountId, clienttype,
classification, legalForm, clientNonPerson, isStaff);
@@ -0,0 +1,169 @@
--
-- 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.
--
create table if not exists scheduled_email_campaign (
id bigint(20) NOT NULL AUTO_INCREMENT,
campaign_name varchar(100) NOT NULL,
campaign_type int NOT NULL,
businessRule_id int NOT NULL,
param_value text,
status_enum int NOT NULL,
closedon_date date,
closedon_userid bigint(20),
submittedon_date date,
submittedon_userid bigint(20),
approvedon_date date,
approvedon_userid bigint(20),
recurrence varchar(100),
next_trigger_date datetime,
last_trigger_date datetime,
recurrence_start_date datetime,
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_status varchar(10) null,
previous_run_error_log text null,
previous_run_error_message text null,
is_visible tinyint(1) null,
foreign key (submittedon_userid) references m_appuser(id),
foreign key (stretchy_report_id) references stretchy_report(id),
PRIMARY KEY (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS scheduled_email_messages_outbound (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_id` bigint(20) DEFAULT NULL,
`client_id` bigint(20) DEFAULT NULL,
`staff_id` bigint(20) DEFAULT NULL,
`email_campaign_id` bigint(20) DEFAULT NULL,
`status_enum` int(5) NOT NULL DEFAULT '100',
`email_address` varchar(50) NOT NULL,
`email_subject` varchar(50) NOT NULL,
`message` text NOT NULL,
`campaign_name` varchar(200) DEFAULT NULL,
`submittedon_date` date,
`error_message` text,
PRIMARY KEY (`id`),
KEY `SEFKGROUP000000001` (`group_id`),
KEY `SEFKCLIENT00000001` (`client_id`),
key `SEFKSTAFF000000001` (`staff_id`),
CONSTRAINT `SEFKGROUP000000001` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`),
CONSTRAINT `SEFKCLIENT00000001` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`),
CONSTRAINT `SEFKSTAFF000000001` FOREIGN KEY (`staff_id`) REFERENCES `m_staff` (`id`),
CONSTRAINT `fk_schedule_email_campign` FOREIGN KEY (`email_campaign_id`) REFERENCES `scheduled_email_campaign` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table if not exists scheduled_email_configuration (
id int primary key auto_increment,
name varchar(50) not null,
`value` varchar(200) null,
constraint unique_name unique (name)
);
DELETE FROM `m_permission` WHERE `code`='READ_EMAIL';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'READ_EMAIL', 'EMAIL', 'READ', 0);
DELETE FROM `m_permission` WHERE `code`='CREATE_EMAIL';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'CREATE_EMAIL', 'EMAIL', 'CREATE', 0);
DELETE FROM `m_permission` WHERE `code`='CREATE_EMAIL_CHECKER';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'CREATE_EMAIL_CHECKER', 'EMAIL', 'CREATE_CHECKER', 0);
DELETE FROM `m_permission` WHERE `code`='UPDATE_EMAIL';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'UPDATE_EMAIL', 'EMAIL', 'UPDATE', 0);
DELETE FROM `m_permission` WHERE `code`='UPDATE_EMAIL_CHECKER';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'UPDATE_EMAIL_CHECKER', 'EMAIL', 'UPDATE_CHECKER', 0);
DELETE FROM `m_permission` WHERE `code`='DELETE_EMAIL';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'DELETE_EMAIL', 'EMAIL', 'DELETE', 0);
DELETE FROM `m_permission` WHERE `code`='DELETE_EMAIL_CHECKER';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'DELETE_EMAIL_CHECKER', 'EMAIL', 'DELETE_CHECKER', 0);
DELETE FROM `m_permission` WHERE `code`='READ_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'READ_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'READ', 0);
DELETE FROM `m_permission` WHERE `code`='CREATE_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'CREATE_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'CREATE', 0);
DELETE FROM `m_permission` WHERE `code`='CREATE_EMAIL_CAMPAIGN_CHECKER';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'CREATE_EMAIL_CAMPAIGN_CHECKER', 'EMAIL_CAMPAIGN', 'CREATE_CHECKER', 0);
DELETE FROM `m_permission` WHERE `code`='UPDATE_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'UPDATE_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'UPDATE', 0);
DELETE FROM `m_permission` WHERE `code`='UPDATE_EMAIL_CAMPAIGN_CHECKER';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'UPDATE_EMAIL_CAMPAIGN_CHECKER', 'EMAIL_CAMPAIGN', 'UPDATE_CHECKER', 0);
DELETE FROM `m_permission` WHERE `code`='DELETE_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'DELETE_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'DELETE', 0);
DELETE FROM `m_permission` WHERE `code`='DELETE_EMAIL_CAMPAIGN_CHECKER';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'DELETE_EMAIL_CAMPAIGN_CHECKER', 'EMAIL_CAMPAIGN', 'DELETE_CHECKER', 0);
DELETE FROM `m_permission` WHERE `code`='CLOSE_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'CLOSE_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'CLOSE', 0);
DELETE FROM `m_permission` WHERE `code`='ACTIVATE_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'ACTIVATE_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'ACTIVATE', 0);
DELETE FROM `m_permission` WHERE `code`='REACTIVATE_EMAIL_CAMPAIGN';
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'REACTIVATE_EMAIL_CAMPAIGN', 'EMAIL_CAMPAIGN', 'REACTIVATE', 0);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('organisation', 'READ_EMAIL_CONFIGURATION', 'EMAIL_CONFIGURATION', 'READ', 0),
('organisation', 'UPDATE_EMAIL_CONFIGURATION', 'EMAIL_CONFIGURATION', 'UPDATE', 0);
Alter table m_client
ADD Column email_address varchar(150);
Alter table m_staff
ADD Column email_address varchar(150);
insert into job (name, display_name, cron_expression, create_time)
values ('Execute Email', 'Execute Email', '0 0/10 * * * ?', NOW());
insert into job (name, display_name, cron_expression, create_time)
values ('Update Email Outbound with campaign message', 'Update Email Outbound with campaign message', '0 0/15 * * * ?', NOW());
INSERT INTO `scheduled_email_configuration` (`name`)
VALUES ('SMTP_SERVER'),
('SMTP_PORT'),('SMTP_USERNAME'), ('SMTP_PASSWORD');
@@ -0,0 +1,127 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.
--
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Active Clients - Email', 'Email', 'SELECT mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress,\nmo.name as officename, ifnull(od.phoneNumber,\'\') as officenummber\nFROM\nm_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nWHERE mc.status_enum = 300 and mc.email_address is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)', 'All clients with the status ‘Active’', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Prospective Clients - Email', 'Email', 'select mc.id,mo.name as OfficeName, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress,ifnull(od.phoneNumber,\'\') as officenummber\nFROM\nm_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nwhere\n(\nSELECT count(ml.id) as loansCount\nFROM m_loan ml\nWhere ml.client_id = mc.id and (ml.writtenoffon_date>=CURDATE() OR ml.writtenoffon_date IS NULL)\nAND disbursedon_date<=CURDATE()\n) = 0\nAND mc.activation_date<CURDATE()\nAND IFNULL(DATEDIFF(CURDATE(), mc.`activation_date`),0) < 90\nAND (mc.closedon_date >CURDATE() OR mc.closedon_date IS NULL)\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)', 'All clients with the status ‘Active’ who have never had a loan before', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Active Loan Clients - Email', 'Email', '(select mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,ifnull(mg.display_name,\'individual\') as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.client_id = mc.id\nLeft join m_group mg on mg.id = mgc.group_id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nwhere mc.status_enum = 300\nand email_address is not null\nand ml.id is not null\nand ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\nand ifnull(ml.loan_counter,0) between ${cycleX} and ${cycleY}\ngroup by mc.id,ml.id )\nunion\n(\nselect mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mg.display_name as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_group mg on mg.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.group_id = mg.id\nleft join m_client as mc on mgc.client_id = mc.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on ml.group_id = mg.id and ml.group_id is not null\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nwhere mc.status_enum = 300 and mg.status_enum = 300\nand email_address is not null\nand ml.id is not null\nand ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mg.staff_id = ${staffId} or ${staffId} = -1)\nand ml.group_id is not null\nand ifnull(ml.loan_counter,0) between ${cycleX} and ${cycleY}\ngroup by mc.id,ml.id\n)', 'All clients with an outstanding loan', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loans in arrears - Email', 'Email', '(select mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,ifnull(laa.total_overdue_derived,0) as TotalDue,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,ifnull(mg.display_name,\'individual\') as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.client_id = mc.id\nLeft join m_group mg on mg.id = mgc.group_id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nright join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day)) and mls.`completed_derived` = 0\nand mls.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate <= CURDATE() and completed_derived=0)\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\ngroup by ml.id )\nunion\n(\nselect mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,ifnull(laa.total_overdue_derived,0) as TotalDue,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mg.display_name as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_group mg on mg.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.group_id = mg.id\nleft join m_client as mc on mgc.client_id = mc.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on ml.group_id = mg.id and ml.group_id is not null\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nright join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day)) and mls.`completed_derived` = 0\nand mls.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate <= CURDATE() and completed_derived=0)\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300 and mg.status_enum = 300 and ml.group_id is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\ngroup by mc.id,ml.id)', 'All clients with an outstanding loan in arrears between X and Y days', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loans disbursed to clients - Email', 'Email', '(select mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mls.duedate,(ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) as TotalDue,ifnull(mg.display_name,\'individual\') as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.client_id = mc.id\nLeft join m_group mg on mg.id = mgc.group_id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id and (ml.`disbursedon_date` BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day))\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id\nand mls.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate >= CURDATE() and obligations_met_on_date is null)\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\nand IFNULL(ml.loan_counter,0) between ${cycleX} and ${cycleY}\ngroup by ml.id )\nunion\n(select mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mls.duedate,(ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) as TotalDue,mg.display_name as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_group mg on mg.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.group_id = mg.id\nleft join m_client as mc on mgc.client_id = mc.id\n\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on ml.group_id = mg.id and (ml.`disbursedon_date` BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day))\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id\nand mls.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate >= CURDATE() and obligations_met_on_date is null)\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300 and mg.status_enum = 300\nand (mo.id = ${officeId} or ${officeId} = -1) and ml.group_id is not null\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\nand IFNULL(ml.loan_counter,0) between ${cycleX} and ${cycleY}\ngroup by ml.id,mc.id)', 'All clients who have had a loan disbursed to them in the last X to Y days', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan payments due - Email', 'Email', '(select mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount,ifnull(od.phoneNumber,\'\') as officenummber,\nround(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,\n(ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) + ifnull(laa.total_overdue_derived,0) as TotalDue,\nifnull(laa.total_overdue_derived,0) as TotalOverdue,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,ifnull(mg.display_name,\'individual\') as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.client_id = mc.id\nLeft join m_group mg on mg.id = mgc.group_id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nleft join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_ADD(CURDATE(), Interval ${fromX} Day) and DATE_ADD(CURDATE(), Interval ${toY} Day)) and mls.`completed_derived` = 0\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1) group by ml.id )\nunion\n(\nselect mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount,ifnull(od.phoneNumber,\'\') as officenummber,\nround(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,\n(ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) + ifnull(laa.total_overdue_derived,0) as TotalDue,\nifnull(laa.total_overdue_derived,0) as TotalOverdue,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mg.display_name as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_group mg on mg.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.group_id = mg.id\nleft join m_client as mc on mgc.client_id = mc.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on ml.group_id = mg.id and ml.group_id is not null\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nleft join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_ADD(CURDATE(), Interval ${fromX} Day) and DATE_ADD(CURDATE(), Interval ${toY} Day)) and mls.`completed_derived` = 0\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300 and mg.status_enum = 300 and ml.group_id is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1) group by ml.id,mc.id)', 'All clients with an unpaid installment due on their loan between X and Y days', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Dormant Prospects - Email', 'Email', 'select mo.name as OfficeName, mc.firstname, ifnull(mc.middlename,\"\") as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress,ifnull(od.phoneNumber,\"\") as officenummber\nFROM\nm_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \"%\")\nAND ounder.hierarchy like CONCAT(\".\", \"%\")\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nwhere\n(\nSELECT count(ml.id) as loansCount\nFROM m_loan ml\nWHERE ml.client_id = mc.id\nand (ml.writtenoffon_date>=CURDATE()\nOR ml.writtenoffon_date IS NULL )\nAND disbursedon_date<=CURDATE()\n) = 0\nAND IFNULL(DATEDIFF(CURDATE(), mc.`activation_date`),0) >90\nAND (mc.closedon_date >CURDATE() OR mc.closedon_date IS NULL)\nAND mc.activation_date<CURDATE()\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)', 'All individuals who have not yet received a loan but were also entered into the system more than 3 months', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Active Group Leaders - Email', 'Email', 'SELECT mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress,\nmo.name as officename, ifnull(od.phoneNumber,\'\') as officenummber\nFROM\nm_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_group_roles mgr on mgr.client_id = mc.id\nright join m_code_value mcv on mcv.id = mgr.role_cv_id and mcv.code_value = \"Chairman\"\nWHERE mc.status_enum = 300 and mc.email_address is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1) group by mc.id', 'All active group chairmen ', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Payments Due (Overdue Loans) - Email', 'Email', 'select mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount,ifnull(od.phoneNumber,\'\') as officenummber,\nround(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,\nround((ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) + ifnull(laa.total_overdue_derived,0), ml.currency_digits) as TotalDue,\nround(ifnull(laa.total_overdue_derived,0), ml.currency_digits) as TotalOverdue,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \', \') ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nleft join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_ADD(CURDATE(), Interval ${fromX} Day) and DATE_ADD(CURDATE(), Interval ${toY} Day)) and mls.`completed_derived` = 0\nright join m_loan_repayment_schedule as mls1 on ml.id = mls1.loan_id and (mls1.`duedate` BETWEEN DATE_SUB(CURDATE(), Interval ${overdueY} Day) and DATE_SUB(CURDATE(), Interval ${overdueX} Day)) and mls1.`completed_derived` = 0\nand mls1.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate <= CURDATE() and completed_derived=0)\nwhere status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand ml.id in (select mla.loan_id from m_loan_arrears_aging mla)\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1) group by ml.id', 'Loan Payments Due between X to Y days for clients in arrears between X and Y days', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Payments Received (Active Loans) - Email', 'Email', 'select mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId, round(mlt.amountPaid, ml.currency_digits) as repaymentAmount\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id\nright join(\nselect sum(mlt.amount) as amountPaid,mlt.id,mlt.loan_id\nfrom m_loan_transaction mlt\nwhere mlt.is_reversed = 0 and mlt.transaction_date BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day) and mlt.transaction_type_enum = 2\ngroup by mlt.loan_id\n) as mlt on mlt.loan_id = ml.id\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\nand ml.id not in (select mla.loan_id from m_loan_arrears_aging mla)\ngroup by ml.id', 'Payments received in the last X to Y days for any loan with the status Active (on-time)', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Payments Received (Overdue Loans) - Email', 'Email', 'select ml.id as loanId,mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId, round(mlt.amountPaid, ml.currency_digits) as repaymentAmount\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id\nright join(\nselect sum(mlt.amount) as amountPaid,mlt.id,mlt.loan_id\nfrom m_loan_transaction mlt\nwhere mlt.is_reversed = 0 and mlt.transaction_date BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day) and mlt.transaction_type_enum = 2\ngroup by mlt.loan_id\n) as mlt on mlt.loan_id = ml.id\nright join m_loan_repayment_schedule as mls1 on ml.id = mls1.loan_id and (mls1.`duedate` BETWEEN DATE_SUB(CURDATE(), Interval ${overdueY} Day) and DATE_SUB(CURDATE(), Interval ${overdueX} Day)) and mls1.`completed_derived` = 0\nand mls1.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate <= CURDATE() and completed_derived=0)\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\nand ml.id in (select mla.loan_id from m_loan_arrears_aging mla)\ngroup by ml.id', 'Payments received in the last X to Y days for any loan with the status Overdue (arrears) between X and Y days', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Fully Repaid - Email', 'Email', '(select mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, ml.principal_amount as LoanAmount, ml.`total_outstanding_derived` as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \", \") ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mls.duedate,(ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) as TotalDue,ifnull(mg.display_name,\'individual\') as groupName,ml.total_repayment_derived as totalFullyPaid\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.client_id = mc.id\nLeft join m_group mg on mg.id = mgc.group_id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id and (ml.`loan_status_id` = 600 or ml.`loan_status_id` = 700 )\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id\nand mls.installment = (SELECT Max(installment) from m_loan_repayment_schedule where loan_id = ml.id and obligations_met_on_date BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day) and completed_derived is true and obligations_met_on_date is not null)\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nwhere mc.status_enum = 300 and email_address is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\ngroup by ml.id )\nunion\n(select mc.id, mc.firstname, ifnull(middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, ml.principal_amount as LoanAmount, ml.`total_outstanding_derived` as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \", \") ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mls.duedate,(ifnull(mls.principal_amount,0) + ifnull(mls.interest_amount,0) + ifnull(mls.fee_charges_amount,0) + ifnull(mls.penalty_charges_amount,0)) - (ifnull(mls.principal_completed_derived,0) + ifnull(mls.interest_completed_derived,0) + ifnull(mls.fee_charges_completed_derived,0) + ifnull(mls.penalty_charges_completed_derived,0)) as TotalDue,mg.display_name as groupName,ml.total_repayment_derived as totalFullyPaid\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_group mg on mg.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.group_id = mg.id\nleft join m_client as mc on mgc.client_id = mc.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on ml.group_id = mg.id and (ml.`loan_status_id` = 600 or ml.`loan_status_id` = 700 )\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id\nand mls.installment = (SELECT Max(installment) from m_loan_repayment_schedule where loan_id = ml.id and obligations_met_on_date BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day) and completed_derived is true and obligations_met_on_date is not null)\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nwhere mc.status_enum = 300 and email_address is not null\nand (mo.id = ${officeId} or ${officeId} = -1) and ml.group_id is not null\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\ngroup by ml.id,mc.id)\n', 'All loans that have been fully repaid (Closed or Overpaid) in the last X to Y days', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loans Outstanding after final instalment date - Email', 'Email', '(select mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, ml.principal_amount as LoanAmount, ml.`total_outstanding_derived` as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,ifnull(laa.total_overdue_derived,0) as TotalDue,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \", \") ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,ifnull(mg.display_name,\'individual\') as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.client_id = mc.id\nLeft join m_group mg on mg.id = mgc.group_id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.id = ml.client_id and ml.`loan_status_id` = 300 and curdate() > ml.expected_maturedon_date and (ml.expected_maturedon_date BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day))\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nright join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day)) and mls.`completed_derived` = 0\nwhere mc.status_enum = 300 and email_address is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\ngroup by ml.id )\nunion\n(\nselect mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, ml.principal_amount as LoanAmount, ml.`total_outstanding_derived` as LoanOutstanding, ml.`disbursedon_date` as LoanDisbursed, mls.`duedate` as PaymentDueDate,ifnull(laa.total_overdue_derived,0) as TotalDue,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId,ifnull(GROUP_CONCAT(DISTINCT t.lastname SEPARATOR \", \") ,\'\') as guarantorLastname,sum(t.totalGuarantors) as numberOfGuarantors,mg.display_name as groupName\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_group mg on mg.office_id = ounder.id\nLEFT join m_group_client mgc on mgc.group_id = mg.id\nleft join m_client as mc on mgc.client_id = mc.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on ml.group_id = mg.id and ml.group_id is not null and curdate() > ml.expected_maturedon_date and (ml.expected_maturedon_date BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day))\nleft join (\nselect mg.loan_id,mc.lastname,count(mg.id) as totalGuarantors\nfrom m_guarantor mg\nleft join m_client mc on mc.id = mg.entity_id\nright join m_guarantor_funding_details mgf on mgf.guarantor_id = mg.id\nwhere mg.entity_id is not null\ngroup by mg.id\n) as t on t.loan_id = ml.id\nright join m_loan_arrears_aging as laa on laa.loan_id = ml.id\nright join m_loan_repayment_schedule as mls on ml.id = mls.loan_id and (mls.`duedate` BETWEEN DATE_SUB(CURDATE(), Interval ${toY} Day) and DATE_SUB(CURDATE(), Interval ${fromX} Day)) and mls.`completed_derived` = 0\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300 and mg.status_enum = 300 and ml.group_id is not null\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\ngroup by mc.id,ml.id)', 'All active loans (with an outstanding balance) between X to Y days after the final instalment date on their loan schedule', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Happy Birthday - Email', 'Email', 'SELECT mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress,\nmo.name as officename, ifnull(od.phoneNumber,\'\') as officenummber, mc.date_of_birth as dateOfBirth,TIMESTAMPDIFF(YEAR,mc.date_of_birth,CURDATE()) AS age\nFROM\nm_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN m_client mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nWHERE mc.status_enum = 300 and mc.email_address is not null\nand (mo.id = 1 or 1 = -1)\nand (mc.staff_id = -1 or -1 = -1)\n AND (\n MONTH(mc.date_of_birth) = MONTH(NOW())\n AND DAY(mc.date_of_birth) = DAY(NOW())\n ) OR (\n MONTH(mc.date_of_birth) = 2 AND DAY(mc.date_of_birth) = 29\n AND MONTH(NOW()) = 3 AND DAY(NOW()) = 1\n AND (YEAR(NOW()) % 4 = 0)\n AND ((YEAR(NOW()) % 100 != 0) OR (YEAR(NOW()) % 400 = 0))\n )\n group by mc.id', 'This sends a message to all clients with the status Active on their Birthday', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Rejected - Email', 'Email', 'Triggered', 'SELECT mc.id, mc.firstname, mc.middlename as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress, mc.group_name as GroupName,\n mo.name as officename, ifnull(od.phoneNumber,\'\') as officenummber, ml.id as loanId, ml.account_no as accountnumber, ml.principal_amount_proposed as loanamount, ml.annual_nominal_interest_rate as annualinterestrate\n FROM\n m_office mo\n JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\n AND ounder.hierarchy like CONCAT(\'.\', \'%\')\n LEFT JOIN (\n select \n ml.id as loanId, \n ifnull(mc.id,mc2.id) as id, \n ifnull(mc.firstname,mc2.firstname) as firstname, \n ifnull(mc.middlename,ifnull(mc2.middlename,(\'\'))) as middlename, \n ifnull(mc.lastname,mc2.lastname) as lastname, \n ifnull(mc.display_name,mc2.display_name) as display_name, \n ifnull(mc.status_enum,mc2.status_enum) as status_enum,\n ifnull(mc.email_address,mc2.email_address) as email_address,\n ifnull(mg.office_id,mc2.office_id) as office_id,\n ifnull(mg.staff_id,mc2.staff_id) as staff_id,\n mg.id as group_id, \nmg.display_name as group_name\n from\n m_loan ml\n left join m_group mg on mg.id = ml.group_id\n left join m_group_client mgc on mgc.group_id = mg.id\n left join m_client mc on mc.id = mgc.client_id\n left join m_client mc2 on mc2.id = ml.client_id\n order by loanId\n ) mc on mc.office_id = ounder.id\n left join ml_office_details as od on od.office_id = mo.id\n left join m_loan ml on ml.id = mc.loanId\n WHERE mc.status_enum = 300 and mc.email_address is not null\n and (mo.id = ${officeId} or ${officeId} = -1)\n and (mc.staff_id = ${staffId} or ${staffId} = -1)\nand (ml.id = ${loanId} or ${loanId} = -1)\nand (mc.id = ${clientId} or ${clientId} = -1)\nand (mc.group_id = ${groupId} or ${groupId} = -1) \nand (ml.loan_type_enum = ${loanType} or ${loanType} = -1)', 'Loan and client data of rejected loan', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Approved - Email', 'Email', 'Triggered', 'SELECT mc.id, mc.firstname, mc.middlename as middlename, mc.lastname, mc.display_name as FullName, mc.email_address as EmailAddress, mc.group_name as GroupName,\n mo.name as officename, ifnull(od.phoneNumber,\'\') as officenummber, ml.id as loanId, ml.account_no as accountnumber, ml.principal_amount_proposed as loanamount, ml.annual_nominal_interest_rate as annualinterestrate\n FROM\n m_office mo\n JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\n AND ounder.hierarchy like CONCAT(\'.\', \'%\')\n LEFT JOIN (\n select \n ml.id as loanId, \n ifnull(mc.id,mc2.id) as id, \n ifnull(mc.firstname,mc2.firstname) as firstname, \n ifnull(mc.middlename,ifnull(mc2.middlename,(\'\'))) as middlename, \n ifnull(mc.lastname,mc2.lastname) as lastname, \n ifnull(mc.display_name,mc2.display_name) as display_name, \n ifnull(mc.status_enum,mc2.status_enum) as status_enum,\n ifnull(mc.email_address,mc2.email_address) as email_address,\n ifnull(mg.office_id,mc2.office_id) as office_id,\n ifnull(mg.staff_id,mc2.staff_id) as staff_id,\n mg.id as group_id, \nmg.display_name as group_name\n from\n m_loan ml\n left join m_group mg on mg.id = ml.group_id\n left join m_group_client mgc on mgc.group_id = mg.id\n left join m_client mc on mc.id = mgc.client_id\n left join m_client mc2 on mc2.id = ml.client_id\n order by loanId\n ) mc on mc.office_id = ounder.id\n left join ml_office_details as od on od.office_id = mo.id\n left join m_loan ml on ml.id = mc.loanId\n WHERE mc.status_enum = 300 and mc.email_address is not null\n and (mo.id = ${officeId} or ${officeId} = -1)\n and (mc.staff_id = ${staffId} or ${staffId} = -1)\nand (ml.id = ${loanId} or ${loanId} = -1)\nand (mc.id = ${clientId} or ${clientId} = -1)\nand (mc.group_id = ${groupId} or ${groupId} = -1)\nand (ml.loan_type_enum = ${loanType} or ${loanType} = -1)', 'Loan and client data of approved loan', '0', '0');
INSERT INTO `stretchy_report` (`report_name`, `report_type`, `report_subtype`, `report_sql`, `description`, `core_report`, `use_report`) VALUES ('Loan Repayment - Email', 'Email', 'Triggered', 'select ml.id as loanId,mc.id, mc.firstname, ifnull(mc.middlename,\'\') as middlename, mc.lastname, mc.display_name as FullName, email_address as EmailAddress, mc.group_name as GroupName, round(ml.principal_amount, ml.currency_digits) as LoanAmount, round(ml.`total_outstanding_derived`, ml.currency_digits) as LoanOutstanding,\nifnull(od.phoneNumber,\'\') as officenummber,ml.`account_no` as LoanAccountId, round(mlt.amountPaid, ml.currency_digits) as repaymentAmount\nFROM m_office mo\nJOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, \'%\')\nAND ounder.hierarchy like CONCAT(\'.\', \'%\')\nLEFT JOIN (\n select \n ml.id as loanId, \n ifnull(mc.id,mc2.id) as id, \n ifnull(mc.firstname,mc2.firstname) as firstname, \n ifnull(mc.middlename,ifnull(mc2.middlename,(\'\'))) as middlename, \n ifnull(mc.lastname,mc2.lastname) as lastname, \n ifnull(mc.display_name,mc2.display_name) as display_name, \n ifnull(mc.status_enum,mc2.status_enum) as status_enum,\n ifnull(mc.email_address,mc2.email_address) as email_address,\n ifnull(mg.office_id,mc2.office_id) as office_id,\n ifnull(mg.staff_id,mc2.staff_id) as staff_id,\n mg.id as group_id, \nmg.display_name as group_name\n from\n m_loan ml\n left join m_group mg on mg.id = ml.group_id\n left join m_group_client mgc on mgc.group_id = mg.id\n left join m_client mc on mc.id = mgc.client_id\n left join m_client mc2 on mc2.id = ml.client_id\n order by loanId\n ) mc on mc.office_id = ounder.id\nleft join ml_office_details as od on od.office_id = mo.id\nright join m_loan as ml on mc.loanId = ml.id\nright join(\nselect mlt.amount as amountPaid,mlt.id,mlt.loan_id\nfrom m_loan_transaction mlt\nwhere mlt.is_reversed = 0 \ngroup by mlt.loan_id\n) as mlt on mlt.loan_id = ml.id\nright join m_loan_repayment_schedule as mls1 on ml.id = mls1.loan_id and mls1.`completed_derived` = 0\nand mls1.installment = (SELECT MIN(installment) from m_loan_repayment_schedule where loan_id = ml.id and duedate <= CURDATE() and completed_derived=0)\nwhere mc.status_enum = 300 and email_address is not null and ml.`loan_status_id` = 300\nand (mo.id = ${officeId} or ${officeId} = -1)\nand (mc.staff_id = ${staffId} or ${staffId} = -1)\nand (ml.loan_type_enum = ${loanType} or ${loanType} = -1)\nand ml.id in (select mla.loan_id from m_loan_arrears_aging mla)\ngroup by ml.id', 'Loan Repayment', '0', '0');
INSERT INTO `stretchy_report_parameter` (`report_id`, `parameter_id`, `report_parameter_name`)
VALUES
( (SELECT id from stretchy_report where report_name = 'Active Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Active Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Prospective Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Prospective Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Active Loan Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Active Loan Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Active Loan Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'cycleX'), 'Cycle X'),
( (SELECT id from stretchy_report where report_name = 'Active Loan Clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'cycleY'), 'Cycle Y'),
( (SELECT id from stretchy_report where report_name = 'Loans in arrears - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loans in arrears - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loans in arrears - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loans in arrears - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loans disbursed to clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loans disbursed to clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loans disbursed to clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loans disbursed to clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loans disbursed to clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'cycleX'), 'Cycle X'),
( (SELECT id from stretchy_report where report_name = 'Loans disbursed to clients - Email'), (SELECT id from stretchy_parameter where parameter_name = 'cycleY'), 'Cycle Y'),
( (SELECT id from stretchy_report where report_name = 'Loan payments due - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan payments due - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan payments due - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loan payments due - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Dormant Prospects - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Dormant Prospects - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Active Group Leaders - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Active Group Leaders - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Due (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Due (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Due (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Due (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Due (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'overdueX'), 'overdueX'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Due (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'overdueY'), 'overdueY'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Active Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Active Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Active Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Active Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'overdueX'), 'overdueX'),
( (SELECT id from stretchy_report where report_name = 'Loan Payments Received (Overdue Loans) - Email'), (SELECT id from stretchy_parameter where parameter_name = 'overdueY'), 'overdueY'),
( (SELECT id from stretchy_report where report_name = 'Happy Birthday - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Happy Birthday - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Fully Repaid - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Fully Repaid - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Fully Repaid - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loan Fully Repaid - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loans Outstanding after final instalment date - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loans Outstanding after final instalment date - Email'), (SELECT id from stretchy_parameter where parameter_name = 'toY'), 'toY'),
( (SELECT id from stretchy_report where report_name = 'Loans Outstanding after final instalment date - Email'), (SELECT id from stretchy_parameter where parameter_name = 'fromX'), 'fromX'),
( (SELECT id from stretchy_report where report_name = 'Loans Outstanding after final instalment date - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Rejected - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Rejected - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Rejected - Email'), (SELECT id from stretchy_parameter where parameter_name = 'DefaultLoan'), 'loanId'),
( (SELECT id from stretchy_report where report_name = 'Loan Rejected - Email'), (SELECT id from stretchy_parameter where parameter_name = 'DefaultClient'), 'clientId'),
( (SELECT id from stretchy_report where report_name = 'Loan Rejected - Email'), (SELECT id from stretchy_parameter where parameter_name = 'DefaultGroup'), 'groupId'),
( (SELECT id from stretchy_report where report_name = 'Loan Rejected - Email'), (SELECT id from stretchy_parameter where parameter_name = 'SelectLoanType'), 'loanType'),
( (SELECT id from stretchy_report where report_name = 'Loan Approved - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Approved - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Approved - Email'), (SELECT id from stretchy_parameter where parameter_name = 'DefaultLoan'), 'loanId'),
( (SELECT id from stretchy_report where report_name = 'Loan Approved - Email'), (SELECT id from stretchy_parameter where parameter_name = 'DefaultClient'), 'clientId'),
( (SELECT id from stretchy_report where report_name = 'Loan Approved - Email'), (SELECT id from stretchy_parameter where parameter_name = 'DefaultGroup'), 'groupId'),
( (SELECT id from stretchy_report where report_name = 'Loan Approved - Email'), (SELECT id from stretchy_parameter where parameter_name = 'SelectLoanType'), 'loanType'),
( (SELECT id from stretchy_report where report_name = 'Loan Repayment - Email'), (SELECT id from stretchy_parameter where parameter_name = 'OfficeIdSelectOne'), 'Office'),
( (SELECT id from stretchy_report where report_name = 'Loan Repayment - Email'), (SELECT id from stretchy_parameter where parameter_name = 'LoanOfficerSelectOneRec'), 'Loanofficer'),
( (SELECT id from stretchy_report where report_name = 'Loan Repayment - Email'), (SELECT id from stretchy_parameter where parameter_name = 'SelectLoanType'), 'loanType');
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES
('report', 'READ_Active Clients - Email', 'Active Clients - Email', 'READ', 0),
('report', 'READ_Prospective Clients - Email', 'Prospective Clients - Email', 'READ', 0),
('report', 'READ_Active Loan Clients - Email', 'Active Loan Clients - Email', 'READ', 0),
('report', 'READ_Loans in arrears - Email', 'Loans in arrears - Email', 'READ', 0),
('report', 'READ_Loans disbursed to clients - Email', 'Loans disbursed to clients - Email', 'READ', 0),
('report', 'READ_Loan payments due - Email', 'Loan payments due - Email', 'READ', 0),
('report', 'READ_Dormant Prospects - Email', 'Dormant Prospects - Email', 'READ', 0),
('report', 'READ_Active Group Leaders - Email', 'Active Group Leaders - Email', 'READ', 0),
('report', 'READ_Loan Payments Due (Overdue Loans) - Email', 'Loan Payments Due (Overdue Loans) - Email', 'READ', 0),
('report', 'READ_Loan Payments Received (Active Loans) - Email', 'Loan Payments Received (Active Loans) - Email', 'READ', 0),
('report', 'READ_Loan Payments Received (Overdue Loans) - Email', 'Loan Payments Received (Overdue Loans) - Email', 'READ', 0),
('report', 'READ_Loan Fully Repaid - Email', 'Loan Fully Repaid - Email', 'READ', 0),
('report', 'READ_Loans Outstanding after final instalment date - Email', 'Loans Outstanding after final instalment date - Email', 'READ', 0),
('report', 'READ_Happy Birthday - Email', 'Happy Birthday - Email', 'READ', 0),
('report', 'READ_Loan Rejected - Email', 'Loan Rejected - Email', 'READ', 0),
('report', 'READ_Loan Approved - Email', 'Loan Approved - Email', 'READ', 0),
('report', 'READ_Loan Repayment - Email', 'Loan Repayment - Email', 'READ', 0);