diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java index 0ad36120c..a2ac7f0c5 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java +++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java @@ -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; } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/EmailApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/EmailApiConstants.java new file mode 100644 index 000000000..1307467ac --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/EmailApiConstants.java @@ -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 CREATE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName, + dateFormatParamName, groupIdParamName, clientIdParamName, staffIdParamName, messageParamName)); + + public static final Set 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"; + +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/ScheduledEmailConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/ScheduledEmailConstants.java new file mode 100644 index 000000000..950f232f0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/ScheduledEmailConstants.java @@ -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 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 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 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"; +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java new file mode 100644 index 000000000..575ee37f6 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailApiResource.java @@ -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 toApiJsonSerializer; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final EmailConfigurationReadPlatformService emailConfigurationReadPlatformService; + + @Autowired + public EmailApiResource(final PlatformSecurityContext context, final EmailReadPlatformService readPlatformService, + final DefaultToApiJsonSerializer 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 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 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 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 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 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); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailCampaignApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailCampaignApiResource.java new file mode 100644 index 000000000..243eefc86 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailCampaignApiResource.java @@ -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 toApiJsonSerializer; + + private final ApiRequestParameterHelper apiRequestParameterHelper; + + private final EmailCampaignReadPlatformService emailCampaignReadPlatformService; + private final FromJsonHelper fromJsonHelper; + + + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final DefaultToApiJsonSerializer emailCampaignDataDefaultToApiJsonSerializer; + private final EmailCampaignWritePlatformService emailCampaignWritePlatformService; + + private final DefaultToApiJsonSerializer previewCampaignMessageDefaultToApiJsonSerializer; + + + @Autowired + public EmailCampaignApiResource(final PlatformSecurityContext context,final DefaultToApiJsonSerializer toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, + final EmailCampaignReadPlatformService emailCampaignReadPlatformService, final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final DefaultToApiJsonSerializer emailCampaignDataDefaultToApiJsonSerializer, + final FromJsonHelper fromJsonHelper, final EmailCampaignWritePlatformService emailCampaignWritePlatformService, + final DefaultToApiJsonSerializer 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 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()); + + } + + + @GET() + @Path("template") + public String template(@Context final UriInfo uriInfo){ + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions); + + final Collection 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); + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailConfigurationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailConfigurationApiResource.java new file mode 100644 index 000000000..e95669401 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/api/EmailConfigurationApiResource.java @@ -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 toApiJsonSerializer; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final EmailConfigurationReadPlatformService emailConfigurationReadPlatformService; + + @Autowired + public EmailConfigurationApiResource(final PlatformSecurityContext context, final EmailReadPlatformService readPlatformService, + final DefaultToApiJsonSerializer 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 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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailBusinessRulesData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailBusinessRulesData.java new file mode 100644 index 000000000..2d7dfb15e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailBusinessRulesData.java @@ -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 reportParamName; + + + + public EmailBusinessRulesData(final Long reportId, final String reportName,final String reportType, final Map 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 reportParamName, + final String reportSubType, final String reportDescription){ + return new EmailBusinessRulesData(reportId,reportName,reportType,reportParamName,reportSubType,reportDescription); + } + + public Map 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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignData.java new file mode 100644 index 000000000..02ff74e72 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignData.java @@ -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;} +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignTimeLine.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignTimeLine.java new file mode 100644 index 000000000..920462690 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignTimeLine.java @@ -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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignValidator.java new file mode 100644 index 000000000..d85f38693 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailCampaignValidator.java @@ -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 supportedParams = new HashSet(Arrays.asList(campaignName, campaignType,localeParamName,dateFormatParamName, + businessRuleId,paramValue,emailMessage,recurrenceStartDate,activationDateParamName,submittedOnDateParamName,closureDateParamName,recurrenceParamName, + emailSubject,stretchyReportId,stretchyReportParamMap,emailAttachmentFileFormatId)); + + public static final Set supportedParamsForUpdate = new HashSet<>(Arrays.asList(campaignName, campaignType,localeParamName,dateFormatParamName, + businessRuleId,paramValue,emailMessage,recurrenceStartDate,activationDateParamName,recurrenceParamName)); + + public static final Set ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, activationDateParamName)); + + public static final Set CLOSE_REQUEST_DATA_PARAMETERS = new HashSet(Arrays.asList(localeParamName, + dateFormatParamName, closureDateParamName)); + + public static final Set PREVIEW_REQUEST_DATA_PARAMETERS= new HashSet(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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.supportedParams); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.supportedParamsForUpdate); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + + 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.PREVIEW_REQUEST_DATA_PARAMETERS); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List dataValidationErrors = new ArrayList(); + 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.CLOSE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.ACTIVATION_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailCampaignValidator.CLOSE_REQUEST_DATA_PARAMETERS); + + final List dataValidationErrors = new ArrayList(); + 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 dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailConfigurationData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailConfigurationData.java new file mode 100644 index 000000000..6d005ea10 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailConfigurationData.java @@ -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; + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailConfigurationValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailConfigurationValidator.java new file mode 100644 index 000000000..3bb267eea --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailConfigurationValidator.java @@ -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 supportedParams = new HashSet(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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, EmailConfigurationValidator.supportedParams); + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final List 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 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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailData.java new file mode 100644 index 000000000..280b4ea25 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailData.java @@ -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 emailAttachmentFileFormatOptions; + private final List 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 emailAttachmentFileFormatOptions, + final List 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 emailAttachmentFileFormatOptions, final List 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 getEmailAttachmentFileFormatOptions() { + return emailAttachmentFileFormatOptions; + } + + public List getStretchyReportParamDateOptions() { + return stretchyReportParamDateOptions; + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailDataValidator.java new file mode 100644 index 000000000..c7f05dafc --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailDataValidator.java @@ -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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeToken, jsonString, + ScheduledEmailConstants.CREATE_REQUEST_PARAMETERS); + + final List 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>() {}.getType(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeToken, jsonString, + ScheduledEmailConstants.UPDATE_REQUEST_PARAMETERS); + + final List 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 validateEmailRecipients(String emailRecipients) { + Set 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 validateStretchyReportParamMap(String stretchyReportParamMap) { + HashMap stretchyReportParamHashMap = new HashMap<>(); + + if (!StringUtils.isEmpty(stretchyReportParamMap)) { + try { + stretchyReportParamHashMap = new ObjectMapper().readValue(stretchyReportParamMap, new TypeReference>(){}); + } + + catch(Exception e) { + stretchyReportParamHashMap = null; + } + } + + return stretchyReportParamHashMap; + } + + private void throwExceptionIfValidationWarningsExist(final List dataValidationErrors) { + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailMessageWithAttachmentData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailMessageWithAttachmentData.java new file mode 100644 index 000000000..935d339d5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/EmailMessageWithAttachmentData.java @@ -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 attachments; + + private EmailMessageWithAttachmentData(final String to, final String text, final String subject, final List 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 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 getAttachments() { + return this.attachments; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/PreviewCampaignMessage.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/PreviewCampaignMessage.java new file mode 100644 index 000000000..06ff76455 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/PreviewCampaignMessage.java @@ -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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/ScheduledEmailEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/ScheduledEmailEnumerations.java new file mode 100644 index 000000000..d2843a3e3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/data/ScheduledEmailEnumerations.java @@ -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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java new file mode 100644 index 000000000..ef87c76a8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaign.java @@ -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 { + + @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 update(JsonCommand command){ + + final Map 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 dataValidationErrors = new ArrayList(); + 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 dataValidationErrors = new ArrayList(); + 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 dataValidationErrors = new ArrayList(); + 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 dataValidationErrors = new ArrayList(); + 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 dataValidationErrors = new ArrayList(); + validateActivationDate(dataValidationErrors); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateReactivate(){ + final List dataValidationErrors = new ArrayList(); + validateReactivationDate(dataValidationErrors); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateClosureDate(){ + final List dataValidationErrors = new ArrayList(); + validateClosureDate(dataValidationErrors); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + + private void validateActivationDate(final List 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 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 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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java new file mode 100644 index 000000000..480ecca7d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignRepository.java @@ -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, JpaSpecificationExecutor { +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignStatus.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignStatus.java new file mode 100644 index 000000000..8e396a3fe --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignStatus.java @@ -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()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignStatusEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignStatusEnumerations.java new file mode 100644 index 000000000..c7fe791c8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignStatusEnumerations.java @@ -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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java new file mode 100644 index 000000000..f443719f8 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailCampaignType.java @@ -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()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailConfiguration.java new file mode 100644 index 000000000..ac451c7fa --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailConfiguration.java @@ -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 { + @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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailConfigurationRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailConfigurationRepository.java new file mode 100644 index 000000000..00ad729b2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailConfigurationRepository.java @@ -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, JpaSpecificationExecutor { + EmailConfiguration findByName(String name); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessage.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessage.java new file mode 100644 index 000000000..6107e20b9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessage.java @@ -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 { + + @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 update(final JsonCommand command) { + + final Map 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();} +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageAssembler.java new file mode 100644 index 000000000..beed4bb33 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageAssembler.java @@ -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; + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageEnumerations.java new file mode 100644 index 000000000..76d9b6174 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageEnumerations.java @@ -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; + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageRepository.java new file mode 100644 index 000000000..b086fc2cd --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageRepository.java @@ -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, JpaSpecificationExecutor { + // no extra behaviour + List findByStatusType(Integer emailMessageStatus); +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageStatusType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageStatusType.java new file mode 100644 index 000000000..54dec8b33 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/EmailMessageStatusType.java @@ -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()); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/ScheduledEmailAttachmentFileFormat.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/ScheduledEmailAttachmentFileFormat.java new file mode 100644 index 000000000..677555a46 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/ScheduledEmailAttachmentFileFormat.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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() }; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/ScheduledEmailStretchyReportParamDateOption.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/ScheduledEmailStretchyReportParamDateOption.java new file mode 100644 index 000000000..39b6e9b7b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/domain/ScheduledEmailStretchyReportParamDateOption.java @@ -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 }; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailBusinessRuleNotFound.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailBusinessRuleNotFound.java new file mode 100644 index 000000000..e8e3d3ff2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailBusinessRuleNotFound.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignMustBeClosedToBeDeletedException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignMustBeClosedToBeDeletedException.java new file mode 100644 index 000000000..4aef1bd7c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignMustBeClosedToBeDeletedException.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignMustBeClosedToEditException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignMustBeClosedToEditException.java new file mode 100644 index 000000000..364468786 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignMustBeClosedToEditException.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignNotFound.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignNotFound.java new file mode 100644 index 000000000..0307fb1b3 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailCampaignNotFound.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailConfigurationNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailConfigurationNotFoundException.java new file mode 100644 index 000000000..32aa906dd --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailConfigurationNotFoundException.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailConfigurationSMTPUsernameNotValid.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailConfigurationSMTPUsernameNotValid.java new file mode 100644 index 000000000..34aab28e9 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailConfigurationSMTPUsernameNotValid.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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 "); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailNotFoundException.java new file mode 100644 index 000000000..c3b63ffcc --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/exception/EmailNotFoundException.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/ActivateEmailCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/ActivateEmailCampaignCommandHandler.java new file mode 100644 index 000000000..3215c1c59 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/ActivateEmailCampaignCommandHandler.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CloseEmailCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CloseEmailCampaignCommandHandler.java new file mode 100644 index 000000000..6f021587c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CloseEmailCampaignCommandHandler.java @@ -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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CreateEmailCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CreateEmailCampaignCommandHandler.java new file mode 100644 index 000000000..10fce846c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CreateEmailCampaignCommandHandler.java @@ -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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CreateEmailCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CreateEmailCommandHandler.java new file mode 100644 index 000000000..8a65aae37 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/CreateEmailCommandHandler.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/DeleteEmailCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/DeleteEmailCampaignCommandHandler.java new file mode 100644 index 000000000..febd4219c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/DeleteEmailCampaignCommandHandler.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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()); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/DeleteEmailCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/DeleteEmailCommandHandler.java new file mode 100644 index 000000000..8608f190d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/DeleteEmailCommandHandler.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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()); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/ReactivateEmailCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/ReactivateEmailCampaignCommandHandler.java new file mode 100644 index 000000000..b8ea3e086 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/ReactivateEmailCampaignCommandHandler.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/UpdateEmailCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/UpdateEmailCampaignCommandHandler.java new file mode 100644 index 000000000..f02d662a4 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/UpdateEmailCampaignCommandHandler.java @@ -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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/UpdateEmailConfigurationCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/UpdateEmailConfigurationCommandHandler.java new file mode 100644 index 000000000..b468d4c2e --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/handler/UpdateEmailConfigurationCommandHandler.java @@ -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); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformService.java new file mode 100644 index 000000000..a8fde906d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformService.java @@ -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 retrieveAll(); + + EmailBusinessRulesData retrieveOneTemplate(Long resourceId); + + EmailCampaignData retrieveOne(Long resourceId); + + Collection retrieveAllCampaign(); + + Collection retrieveAllScheduleActiveCampaign(); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java new file mode 100644 index 000000000..f791d9260 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignReadPlatformServiceImpl.java @@ -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{ + + 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>{ + + 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 extractData(ResultSet rs) throws SQLException, DataAccessException { + List emailBusinessRulesDataList = new ArrayList(); + + EmailBusinessRulesData emailBusinessRulesData = null; + + Map mapOfSameObjects = new HashMap(); + + 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 hashMap = new HashMap(); + 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 hashMap = new HashMap(); + 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 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 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 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 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); + } + + + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java new file mode 100644 index 000000000..66bd97b41 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformCommandHandlerImpl.java @@ -0,0 +1,775 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; +import com.google.gson.Gson; +import org.apache.commons.lang.StringUtils; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; +import org.joda.time.DateTimeZone; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.api.JsonQuery; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; +import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData; +import org.apache.fineract.infrastructure.dataqueries.domain.*; +import org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException; +import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService; +import org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService; +import org.apache.fineract.infrastructure.documentmanagement.contentrepository.FileSystemContentRepository; +import org.apache.fineract.infrastructure.reportmailingjob.data.ReportMailingJobEmailData; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJob; +import org.apache.fineract.infrastructure.reportmailingjob.domain.ReportMailingJobEmailAttachmentFileFormat; +import org.apache.fineract.infrastructure.reportmailingjob.helper.IPv4Helper; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailMessageWithAttachmentData; +import org.apache.fineract.infrastructure.campaigns.email.domain.*; +import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignMustBeClosedToBeDeletedException; +import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignMustBeClosedToEditException; +import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignNotFound; +import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; +import org.apache.fineract.infrastructure.jobs.service.JobName; +import org.apache.fineract.infrastructure.jobs.service.SchedularWritePlatformService; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.infrastructure.campaigns.email.data.PreviewCampaignMessage; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignData; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailCampaignValidator; +import org.apache.fineract.organisation.staff.domain.Staff; +import org.apache.fineract.portfolio.calendar.service.CalendarUtils; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; +import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; +import org.apache.fineract.portfolio.savings.domain.SavingsAccount; +import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepository; +import org.apache.fineract.template.domain.TemplateRepository; +import org.apache.fineract.template.service.TemplateMergeService; +import org.apache.fineract.useradministration.domain.AppUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; +import java.io.*; +import java.util.*; + +@Service +public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampaignWritePlatformService { + + + private final static Logger logger = LoggerFactory.getLogger(EmailCampaignWritePlatformCommandHandlerImpl.class); + + private final PlatformSecurityContext context; + + private final EmailCampaignRepository emailCampaignRepository; + private final EmailCampaignValidator emailCampaignValidator; + private final EmailCampaignReadPlatformService emailCampaignReadPlatformService; + private final ReportRepository reportRepository; + private final TemplateRepository templateRepository; + private final TemplateMergeService templateMergeService; + private final EmailMessageRepository emailMessageRepository; + private final ClientRepositoryWrapper clientRepositoryWrapper; + private final SchedularWritePlatformService schedularWritePlatformService; + private final ReadReportingService readReportingService; + private final GenericDataService genericDataService; + private final FromJsonHelper fromJsonHelper; + private final ReportParameterUsageRepository reportParameterUsageRepository; + private final LoanRepository loanRepository; + private final SavingsAccountRepository savingsAccountRepository; + private final EmailMessageJobEmailService emailMessageJobEmailService; + + + + + @Autowired + public EmailCampaignWritePlatformCommandHandlerImpl(final PlatformSecurityContext context, final EmailCampaignRepository emailCampaignRepository, + final EmailCampaignValidator emailCampaignValidator,final EmailCampaignReadPlatformService emailCampaignReadPlatformService,final ReportParameterUsageRepository reportParameterUsageRepository, + final ReportRepository reportRepository,final TemplateRepository templateRepository, final TemplateMergeService templateMergeService, + final EmailMessageRepository emailMessageRepository,final ClientRepositoryWrapper clientRepositoryWrapper,final SchedularWritePlatformService schedularWritePlatformService, + final ReadReportingService readReportingService, final GenericDataService genericDataService,final FromJsonHelper fromJsonHelper, + final LoanRepository loanRepository,final SavingsAccountRepository savingsAccountRepository,final EmailMessageJobEmailService emailMessageJobEmailService) { + this.context = context; + this.emailCampaignRepository = emailCampaignRepository; + this.emailCampaignValidator = emailCampaignValidator; + this.emailCampaignReadPlatformService = emailCampaignReadPlatformService; + this.reportRepository = reportRepository; + this.templateRepository = templateRepository; + this.templateMergeService = templateMergeService; + this.emailMessageRepository = emailMessageRepository; + this.clientRepositoryWrapper = clientRepositoryWrapper; + this.schedularWritePlatformService = schedularWritePlatformService; + this.readReportingService = readReportingService; + this.genericDataService = genericDataService; + this.fromJsonHelper = fromJsonHelper; + this.reportParameterUsageRepository = reportParameterUsageRepository; + this.loanRepository = loanRepository; + this.savingsAccountRepository = savingsAccountRepository; + this.emailMessageJobEmailService = emailMessageJobEmailService; + } + + @Transactional + @Override + public CommandProcessingResult create(JsonCommand command) { + + final AppUser currentUser = this.context.authenticatedUser(); + + this.emailCampaignValidator.validateCreate(command.json()); + + final Long businessRuleId = command.longValueOfParameterNamed(EmailCampaignValidator.businessRuleId); + + final Report businessRule = this.reportRepository.findOne(businessRuleId); + if(businessRule == null){ + throw new ReportNotFoundException(businessRuleId); + } + + final Long reportId = command.longValueOfParameterNamed(EmailCampaignValidator.stretchyReportId); + + final Report report = this.reportRepository.findOne(reportId); + if(report == null){ + throw new ReportNotFoundException(reportId); + } + //find all report parameters and store them as json string + final Set reportParameterUsages = report.getReportParameterUsages(); + final Map 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 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 campaignParams = new ObjectMapper().readValue(emailParams, new TypeReference>(){}); + + HashMap queryParamForRunReport = new ObjectMapper().readValue(emailParams, new TypeReference>(){}); + + List> runReportObject = this.getRunReportByServiceImpl(campaignParams.get("reportName"),queryParamForRunReport); + + if(runReportObject !=null){ + for(HashMap 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 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 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> getRunReportByServiceImpl(final String reportName,final Map queryParams) throws IOException { + final String reportType ="report"; + + List> resultList = new ArrayList>(); + final GenericResultsetData results = this.readReportingService.retrieveGenericResultSetForSmsEmailCampaign(reportName, + reportType, queryParams); + final String response = this.genericDataService.generateJsonFromGenericResultsetData(results); + resultList = new ObjectMapper().readValue(response, new TypeReference>>(){}); + //loop changes array date to string date + for(HashMap entry : resultList){ + for(Map.Entry 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 campaignParams = new ObjectMapper().readValue(emailParams, new TypeReference>(){}); + + HashMap queryParamForRunReport = new ObjectMapper().readValue(emailParams, new TypeReference>(){}); + + List> runReportObject = this.getRunReportByServiceImpl(campaignParams.get("reportName"),queryParamForRunReport); + + if(runReportObject !=null){ + for(HashMap 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 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 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 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 loans = this.loanRepository.findLoanByClientId(emailMessage.getClient().getId()); + + HashMap 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 savingsAccounts = this.savingsAccountRepository.findSavingAccountByClientId(emailMessage.getClient().getId()); + + HashMap 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 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 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 replaceStretchyParamsWithActualClientParams(final HashMap stretchyParams,final Client client){ + + HashMap actualParams = new HashMap<>(); + + for (Map.Entry 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 validateStretchyReportParamMap(final String stretchyParams){ + + HashMap stretchyReportParamHashMap = new HashMap<>(); + + if (!StringUtils.isEmpty(stretchyParams)) { + try { + stretchyReportParamHashMap = new ObjectMapper().readValue(stretchyParams, new TypeReference>(){}); + } + + catch(Exception e) { + stretchyReportParamHashMap = null; + } + } + + return stretchyReportParamHashMap; + } + + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java new file mode 100644 index 000000000..a86262e39 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailCampaignWritePlatformService.java @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.api.JsonQuery; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; +import org.apache.fineract.infrastructure.campaigns.email.data.PreviewCampaignMessage; + +public interface EmailCampaignWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long resourceId, JsonCommand command); + + CommandProcessingResult delete(Long resourceId); + + CommandProcessingResult activateEmailCampaign(Long campaignId, JsonCommand command); + + CommandProcessingResult closeEmailCampaign(Long campaignId, JsonCommand command); + + CommandProcessingResult reactivateEmailCampaign(Long campaignId, JsonCommand command); + + void storeTemplateMessageIntoEmailOutBoundTable() throws JobExecutionException; + + PreviewCampaignMessage previewMessage(JsonQuery query); + + void sendEmailMessage() throws JobExecutionException; + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java new file mode 100644 index 000000000..cc704e682 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformService.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData; + +import java.util.Collection; + +public interface EmailConfigurationReadPlatformService { + + Collection retrieveAll(); + + EmailConfigurationData retrieveOne(String name); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java new file mode 100644 index 000000000..394e082c0 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationReadPlatformServiceImpl.java @@ -0,0 +1,99 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.campaigns.email.exception.EmailConfigurationNotFoundException; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; + +@Service +public class EmailConfigurationReadPlatformServiceImpl implements EmailConfigurationReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final EmailConfigurationRowMapper emailConfigurationRowMapper; + + @Autowired + public EmailConfigurationReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + this.emailConfigurationRowMapper = new EmailConfigurationRowMapper(); + + } + + private static final class EmailConfigurationRowMapper implements RowMapper { + + 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 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); + } + } + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java new file mode 100644 index 000000000..3afa8d3e5 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformService.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; + +public interface EmailConfigurationWritePlatformService { + + CommandProcessingResult update(JsonCommand command); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java new file mode 100644 index 000000000..508e2832d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailConfigurationWritePlatformServiceImpl.java @@ -0,0 +1,89 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import com.google.gson.JsonElement; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationData; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailConfigurationValidator; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfiguration; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; +import org.apache.fineract.infrastructure.campaigns.email.exception.EmailConfigurationSMTPUsernameNotValid; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +@Service +public class EmailConfigurationWritePlatformServiceImpl implements EmailConfigurationWritePlatformService { + + private final PlatformSecurityContext context; + private final EmailConfigurationRepository repository; + private final EmailConfigurationValidator emailConfigurationValidator; + + @Autowired + public EmailConfigurationWritePlatformServiceImpl(final PlatformSecurityContext context, final EmailConfigurationRepository repository, + final EmailConfigurationValidator emailConfigurationValidator) { + this.context = context; + this.repository = repository; + this.emailConfigurationValidator = emailConfigurationValidator; + } + + @Override + public CommandProcessingResult update(final JsonCommand command) { + + final AppUser currentUser = this.context.authenticatedUser(); + + this.emailConfigurationValidator.validateUpdateConfiguration(command.json()); + final String smtpUsername = command.stringValueOfParameterNamed("SMTP_USERNAME"); + + if(!this.emailConfigurationValidator.isValidEmail(smtpUsername)){ + throw new EmailConfigurationSMTPUsernameNotValid(smtpUsername); + } + + + final Map changes = new HashMap<>(4); + + Collection 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(); + + } + + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java new file mode 100644 index 000000000..37b193b2a --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailService.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + + +import org.apache.fineract.infrastructure.campaigns.email.data.EmailMessageWithAttachmentData; + +public interface EmailMessageJobEmailService { + + void sendEmailWithAttachment(EmailMessageWithAttachmentData emailMessageWithAttachmentData); + +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java new file mode 100644 index 000000000..c8854a270 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailMessageJobEmailServiceImpl.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + + +import org.apache.fineract.infrastructure.campaigns.email.EmailApiConstants; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailMessageWithAttachmentData; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfiguration; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailConfigurationRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import java.io.File; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +@Service +public class EmailMessageJobEmailServiceImpl implements EmailMessageJobEmailService { + + private EmailConfigurationRepository emailConfigurationRepository; + + @Autowired + private EmailMessageJobEmailServiceImpl(final EmailConfigurationRepository emailConfigurationRepository) { + this.emailConfigurationRepository = emailConfigurationRepository; + } + + @Override + public void sendEmailWithAttachment(EmailMessageWithAttachmentData emailMessageWithAttachmentData) { + try{ + JavaMailSenderImpl javaMailSenderImpl = new JavaMailSenderImpl(); + javaMailSenderImpl.setHost(this.getGmailSmtpServer()); + javaMailSenderImpl.setPort(this.getGmailSmtpPort()); + javaMailSenderImpl.setUsername(this.getGmailSmtpUsername()); + javaMailSenderImpl.setPassword(this.getGmailSmtpPassword()); + javaMailSenderImpl.setJavaMailProperties(this.getJavaMailProperties()); + + MimeMessage mimeMessage = javaMailSenderImpl.createMimeMessage(); + + // use the true flag to indicate you need a multipart message + MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true); + + mimeMessageHelper.setTo(emailMessageWithAttachmentData.getTo()); + mimeMessageHelper.setText(emailMessageWithAttachmentData.getText()); + mimeMessageHelper.setSubject(emailMessageWithAttachmentData.getSubject()); + final List 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; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java new file mode 100644 index 000000000..63480235f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformService.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailData; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +public interface EmailReadPlatformService { + + Collection retrieveAll(); + + EmailData retrieveOne(Long resourceId); + + Collection retrieveAllPending(Integer limit); + + Collection retrieveAllSent(Integer limit); + + Collection retrieveAllDelivered(Integer limit); + + Collection retrieveAllFailed(Integer limit); + + Page retrieveEmailByStatus(Integer limit, Integer status, Date dateFrom, Date dateTo); + + List retrieveExternalIdsOfAllSent(Integer limit); +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java new file mode 100644 index 000000000..98d4dfe0b --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailReadPlatformServiceImpl.java @@ -0,0 +1,199 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.joda.time.LocalDate; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.JdbcSupport; +import org.apache.fineract.infrastructure.core.service.Page; +import org.apache.fineract.infrastructure.core.service.PaginationHelper; +import org.apache.fineract.infrastructure.core.service.RoutingDataSource; +import org.apache.fineract.infrastructure.campaigns.email.exception.EmailNotFoundException; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailData; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageEnumerations; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageStatusType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +@Service +public class EmailReadPlatformServiceImpl implements EmailReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + private final EmailMapper emailRowMapper = new EmailMapper(); + private final PaginationHelper paginationHelper = new PaginationHelper<>(); + + + @Autowired + public EmailReadPlatformServiceImpl(final RoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class EmailMapper implements RowMapper { + + 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 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 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 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 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 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 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 retrieveEmailByStatus(final Integer limit, final Integer status,final Date dateFrom, final Date dateTo) { + final StringBuilder sqlBuilder = new StringBuilder(200); + sqlBuilder.append("select SQL_CALC_FOUND_ROWS "); + sqlBuilder.append(this.emailRowMapper.schema()); + if(status !=null){ + sqlBuilder.append(" where emo.status_enum= ? "); + } + String fromDateString = null; + String toDateString = null; + if(dateFrom !=null && dateTo !=null){ + final DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); + fromDateString = df.format(dateFrom); + toDateString = df.format(dateTo); + sqlBuilder.append(" and emo.submittedon_date >= ? and emo.submittedon_date <= ? "); + } + final String sqlPlusLimit = (limit > 0) ? " limit 0, " + limit : ""; + if(!sqlPlusLimit.isEmpty()){ + sqlBuilder.append(sqlPlusLimit); + } + final String sqlCountRows = "SELECT FOUND_ROWS()"; + return this.paginationHelper.fetchPage(this.jdbcTemplate,sqlCountRows,sqlBuilder.toString(),new Object[]{status,fromDateString,toDateString},this.emailRowMapper); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java new file mode 100644 index 000000000..233427c23 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformService.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; + +public interface EmailWritePlatformService { + + CommandProcessingResult create(JsonCommand command); + + CommandProcessingResult update(Long resourceId, JsonCommand command); + + CommandProcessingResult delete(Long resourceId); +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java new file mode 100644 index 000000000..9e23bc608 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/email/service/EmailWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,134 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.campaigns.email.service; + +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; +import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; +import org.apache.fineract.infrastructure.campaigns.email.data.EmailDataValidator; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessage; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageAssembler; +import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; + +@Service +public class EmailWritePlatformServiceJpaRepositoryImpl implements EmailWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(EmailWritePlatformServiceJpaRepositoryImpl.class); + + private final EmailMessageAssembler assembler; + private final EmailMessageRepository repository; + private final EmailDataValidator validator; + + @Autowired + public EmailWritePlatformServiceJpaRepositoryImpl(final EmailMessageAssembler assembler, final EmailMessageRepository repository, + final EmailDataValidator validator) { + this.assembler = assembler; + this.repository = repository; + this.validator = validator; + } + + @Transactional + @Override + public CommandProcessingResult create(final JsonCommand command) { + + try { + this.validator.validateCreateRequest(command); + + final EmailMessage message = this.assembler.assembleFromJson(command); + + // TODO: at this point we also want to fire off request using third + // party service to send Email. + // TODO: decision to be made on wheter we 'wait' for response or use + // 'future/promise' to capture response and update the EmailMessage + // table + this.repository.save(message); + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(message.getId()) // + .build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult update(final Long resourceId, final JsonCommand command) { + + try { + this.validator.validateUpdateRequest(command); + + final EmailMessage message = this.assembler.assembleFromResourceId(resourceId); + final Map changes = message.update(command); + if (!changes.isEmpty()) { + this.repository.save(message); + } + + return new CommandProcessingResultBuilder() // + .withCommandId(command.commandId()) // + .withEntityId(resourceId) // + .with(changes) // + .build(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult delete(final Long resourceId) { + + try { + final EmailMessage message = this.assembler.assembleFromResourceId(resourceId); + this.repository.delete(message); + this.repository.flush(); + } catch (final DataIntegrityViolationException dve) { + handleDataIntegrityIssues(null, dve); + return CommandProcessingResult.empty(); + } + return new CommandProcessingResultBuilder().withEntityId(resourceId).build(); + } + + /* + * Guaranteed to throw an exception no matter what the data integrity issue + * is. + */ + private void handleDataIntegrityIssues(@SuppressWarnings("unused") final JsonCommand command, final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + + if (realCause.getMessage().contains("email_address")) { throw new PlatformDataIntegrityException("error.msg.email.no.email.address.exists", + "The group, client or staff provided has no email address.", "id"); } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.email.unknown.data.integrity.issue", + "Unknown data integrity issue with resource: " + realCause.getMessage()); + } +} \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java index 54e714c3d..8de1bbf53 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java @@ -126,7 +126,7 @@ public class SmsCampaignApiResource { @GET @Produces({ MediaType.APPLICATION_JSON }) - public String retrieveAllCampaigns(@QueryParam("sqlSearch") final String sqlSearch, + public String retrieveAllEmails(@QueryParam("sqlSearch") final String sqlSearch, @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder, @Context final UriInfo uriInfo) { this.platformSecurityContext.authenticatedUser().validateHasReadPermission(SmsCampaignConstants.RESOURCE_NAME); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java index 0084ed6f9..8c9dd3089 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java @@ -514,7 +514,7 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP final String reportType = "report"; List> resultList = new ArrayList<>(); - final GenericResultsetData results = this.readReportingService.retrieveGenericResultSetForSmsCampaign(reportName, reportType, + final GenericResultsetData results = this.readReportingService.retrieveGenericResultSetForSmsEmailCampaign(reportName, reportType, queryParams); try { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java index 34d397f11..a3b14fb89 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java @@ -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, diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java index e39a59169..f909c33b6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/Report.java @@ -276,4 +276,8 @@ public final class Report extends AbstractPersistableCustom { return false; } + + public Set getReportParameterUsages() { + return this.reportParameterUsages; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsage.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsage.java index 4d10fefdd..7228e9c7a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsage.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsage.java @@ -86,4 +86,8 @@ public final class ReportParameterUsage extends AbstractPersistableCustom public void updateParameterName(final String parameterName) { this.reportParameterName = parameterName; } + + public String getReportParameterName() { + return this.reportParameterName; + } } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsageRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsageRepository.java index 8a49331fa..2d47db2fb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsageRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/domain/ReportParameterUsageRepository.java @@ -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, JpaSpecificationExecutor { // no added behaviour + + List findByReport(Report report); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java index a21f645b8..952a36a42 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingService.java @@ -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 getAllowedReportTypes(); - //needed for smsCampaign jobs where securityContext is null - GenericResultsetData retrieveGenericResultSetForSmsCampaign(String name, String type, Map extractedQueryParams); - - String sqlToRunForSmsCampaign(String name, String type, Map queryParams); + //needed for smsCampaign and emailCampaign jobs where securityContext is null + GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String name, String type, Map extractedQueryParams); + + String sqlToRunForSmsEmailCampaign(String name, String type, Map queryParams); + + ByteArrayOutputStream generatePentahoReportAsOutputStream(String reportName, String outputTypeParam, + Map queryParams, Locale locale, AppUser runReportAsUser, StringBuilder errorLog); } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java index f25c371f8..da0bc01fd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadReportingServiceImpl.java @@ -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 queryParams) { + public GenericResultsetData retrieveGenericResultSetForSmsEmailCampaign(String name, String type, Map 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 queryParams) { + public String sqlToRunForSmsEmailCampaign(final String name, final String type, final Map queryParams) { String sql = getSql(name, type); final Set 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 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); + } } + + diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java index f3a160f96..19ad8e14c 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java @@ -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) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/domain/ReportMailingJobEmailAttachmentFileFormat.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/domain/ReportMailingJobEmailAttachmentFileFormat.java new file mode 100644 index 000000000..ef24a20d2 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/domain/ReportMailingJobEmailAttachmentFileFormat.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.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() }; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/helper/IPv4Helper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/helper/IPv4Helper.java new file mode 100644 index 000000000..2281416e1 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/reportmailingjob/helper/IPv4Helper.java @@ -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 ipAddress + * @throws IllegalArgumentException if ipAddress 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 ip + * @throws IllegalArgumentException if ip 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(); + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/Staff.java b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/Staff.java index 64f6f90d0..175fcb797 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/Staff.java +++ b/fineract-provider/src/main/java/org/apache/fineract/organisation/staff/domain/Staff.java @@ -61,6 +61,9 @@ public class Staff extends AbstractPersistableCustom { @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 { return getId().equals(staff.getId()); } + public String emailAddress() { + return emailAddress; + } + public Long officeId() { return this.office.getId(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java index 090cc0a9e..28ed583ae 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientApiConstants.java @@ -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 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, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java index 04b5fe4de..acf0dc79b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java @@ -29,7 +29,7 @@ public class ClientApiCollectionConstants extends ClientApiConstants{ protected static final Set 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 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)); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java index 3827307fc..557725d35 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java @@ -58,6 +58,7 @@ final public class ClientData implements Comparable { 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 { 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 { 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 { 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 { 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 { public static ClientData setParentGroups(final ClientData clientData, final Collection 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 { 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 { 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 { 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 { 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 { 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 { final List 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 { 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 allowedOffices, final Collection groups, final Collection staffOptions, final Collection narrations, @@ -354,6 +358,7 @@ final public class ClientData implements Comparable { 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 { .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 { .append(this.id, rhs.id) // .append(this.displayName, rhs.displayName) // .append(this.mobileNo, rhs.mobileNo) // + .append(this.emailAddress, rhs.emailAddress) // .isEquals(); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java index 08425f7f5..1b1b3938b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java @@ -114,6 +114,9 @@ public final class Client extends AbstractPersistableCustom { @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 { 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 { } 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 { 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 { 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 { 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 { 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 { return this.externalId; } + public void setEmailAddress(final String emailAddress) { + this.emailAddress = emailAddress; + } + public String getDisplayName() { return this.displayName; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java index b5e88259b..4d54c1e96 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -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); diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V322_1__scheduled_email_campaign.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V322_1__scheduled_email_campaign.sql new file mode 100644 index 000000000..f581a0c5c --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V322_1__scheduled_email_campaign.sql @@ -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'); + diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V322_2__email_business_rules.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V322_2__email_business_rules.sql new file mode 100644 index 000000000..990f18d8b --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V322_2__email_business_rules.sql @@ -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_dateCURDATE() 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 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); \ No newline at end of file