diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm
index abd6867f4..c2a06be6b 100644
--- a/api-docs/apiLive.htm
+++ b/api-docs/apiLive.htm
@@ -2064,7 +2064,46 @@
| Currency |
currencies |
@@ -21038,7 +21077,291 @@ Content-Type: application/json
-
+
+
+
+
Create a SMS Campaign
+
+ Mandatory Fields |
+
+ | campaignName, campaignType, triggerType, providerId, runReportId, message |
+
+
+
+
+ Mandatory Fields for Cash based on selected report id |
+
+ | paramValue in json format |
+
+
+
+
+
+ POST https://Domain Name/api/v1/smscampaigns
+ POST smscampaigns
+Content-Type: application/json
+Request Body:
+{
+"campaignName": "Savings Deposit Campaign",
+"campaignType": "SMS",
+"triggerType": 3,
+"providerId": 2,
+"runReportId": 180,
+"paramValue": "{"officeId":1,"loanOfficerId":-1,"transactionId":1,"reportName":"Savings Deposit"}",
+"message": "Hello {fullName} your account is credited with {depositAmount} on {transactionDate}",
+}
+
+
+
+{
+ "resourceId": 1
+}
+
+
+
+
+
+
+
+
+
List SMS Campaigns
+
+ Mandatory Fields |
+
+ |
+
+
+
Example Requests:
+
smscampaigns
+
+
+
+ GET https://Domain Name/api/v1/smscampaigns
+
+
+[{
+ "id": 1,
+ "campaignName": "Savings Deposit Campaign"
+ "campaignType": "SMS",
+ "triggerType": 3,
+ "providerId": 2,
+ "runReportId": 180,
+ "reportName" : "Savings Deposit",
+ "paramValue": "{"officeId":1,"loanOfficerId":-1,"transactionId":1,"reportName":"Savings Deposit"}",
+ "message": "Hello {fullName} your account is credited with {depositAmount} on {transactionDate}",
+ "status" : { "id":"","code":"","value":""}
+
+}]
+
+
+
+
+
+
+
+
Retrieve a Share Product
+
Example Requests:
+
smscampaigns/1
+
+
smscampaigns/1?template=true
+
+
+ GET https://Domain Name/api/v1/smscampaigns/1
+
+{
+ "id": 1,
+ "campaignName": "Savings Deposit Campaign"
+ "campaignType": "SMS",
+ "triggerType": 3,
+ "providerId": 2,
+ "runReportId": 180,
+ "reportName" : "Savings Deposit",
+ "paramValue": "{"officeId":1,"loanOfficerId":-1,"transactionId":1,"reportName":"Savings Deposit"}",
+ "message": "Hello {fullName} your account is credited with {depositAmount} on {transactionDate}",
+ "status" : { "id":"","code":"","value":""}
+}
+In case of Scheduled Campaign
+{
+ "id": 1,
+ "campaignName": "Savings Deposit Campaign"
+ "campaignType": "SMS",
+ "triggerType": 3,
+ "providerId": 2,
+ "runReportId": 180,
+ "reportName" : "Savings Deposit",
+ "paramValue": "{"officeId":1,"loanOfficerId":-1,"transactionId":1,"reportName":"Savings Deposit"}",
+ "message": "Hello {fullName} your account is credited with {depositAmount} on {transactionDate}",
+ "status" : { "id":"","code":"","value":""},
+ "nextTriggerDate": "22 Jan 2017",
+ "lastTriggerDate": "22 Jan 2016",
+ "recurrenceStartDate":"22 Jan 2016",
+ "recurrence": "YEARLY"
+}
+
+
+
+
+
+
+
+
Update a Campaign
+
+
+ PUT https://Domain Name/api/v1/smscampaigns/{campaignId}
+ POST smscampaigns/1
+Content-Type: application/json
+Request Body:
+{
+ "message": "Hello your savings account {savingsAccountNo} debited with {withdrawAmount}"
+
+}
+
+
+{
+ "resourceId": 1,
+ "changes": {
+ "message": "Hello your savings account {savingsAccountNo} debited with {withdrawAmount}"
+ }
+}
+
+
+
+
+
+
+
+
Delete a SMS Campaign
+
+ Note: Only closed SMS Campaigns can be
+ deleted
+
+
+
+
+DELETE https://DomainName/api/v1/smscampaigns/{campaignId}
+
+
+DELETE smscampaigns/1
+Content-Type: application/json
+No Request Body:
+
+
+{
+ "resourceId": 1,
+ "changes": {}
+}
+
+
+
+
+
+
+
+
Activates SMS Campaign
+
+
+
+ POST https://Domain Name/api/v1/smscampaigns/{campaignId}?command=activate
+ POST smscampaigns/1?command=activate
+Content-Type: application/json
+Request Body:
+{
+"activationDate":"01 May 2016",
+"locale":"en",
+"dateFormat":"dd MMMM yyyy"
+}
+
+
+{
+
+ "resourceId": 1,
+ "changes": {
+ "status": {
+ "id": 300,
+ "code": "smsCampaignStatus.active",
+ "value": "Activated",
+ },
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "activationDate": "01 May 2016"
+ }
+}
+
+
+
+
+
+
+
+
Deactivates SMS Campaign
+
+
+
+ POST https://Domain Name/api/v1/smscampaigns/{campaignId}?command=close
+ POST smscampaigns/1?command=close
+Content-Type: application/json
+Request Body:
+{
+"closureDate":"01 May 2016",
+"locale":"en",
+"dateFormat":"dd MMMM yyyy"
+}
+
+
+{
+ "resourceId": 1,
+ "changes": {
+ "status": {
+ "id": 600,
+ "code": "smsCampaignStatus.closed",
+ "value": "Activated",
+ },
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "closureDate": "01 May 2016"
+ }
+}
+
+
+
+
+
+
+
+
Reactivates SMS Campaign
+
+
+
+ POST https://Domain Name/api/v1/smscampaigns/{campaignId}?command=reactivate
+ POST smscampaigns/1?command=reactivate
+Content-Type: application/json
+Request Body:
+{
+"activationDate":"01 May 2016",
+"locale":"en",
+"dateFormat":"dd MMMM yyyy"
+}
+
+
+{
+
+ "resourceId": 1,
+ "changes": {
+ "status": {
+ "id": 300,
+ "code": "smsCampaignStatus.active",
+ "value": "Activated",
+ },
+ "locale": "en",
+ "dateFormat": "dd MMMM yyyy",
+ "activationDate": "01 May 2016"
+ }
+}
+
+
+
+
+
Create a Share Product
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 fb497c04c..ed92a3879 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
@@ -2803,4 +2803,51 @@ public class CommandWrapperBuilder {
this.href = "/reportmailingjobs/" + entityId;
return this;
}
+
+ public CommandWrapperBuilder createSmsCampaign() {
+ this.actionName = "CREATE";
+ this.entityName = "SMSCAMPAIGN";
+ this.entityId = null;
+ this.href = "/smscampaign";
+ return this;
+ }
+
+ public CommandWrapperBuilder updateSmsCampaign(final Long resourceId) {
+ this.actionName = "UPDATE";
+ this.entityName = "SMSCAMPAIGN";
+ this.entityId = resourceId;
+ this.href = "/smscampaign/"+resourceId;
+ return this;
+ }
+
+ public CommandWrapperBuilder activateSmsCampaign(final Long resourceId) {
+ this.actionName = "ACTIVATE";
+ this.entityName = "SMSCAMPAIGN";
+ this.entityId = resourceId;
+ this.href = "/smscampaign/"+resourceId + "?command=activate";
+ return this;
+ }
+
+ public CommandWrapperBuilder closeSmsCampaign(final Long resourceId) {
+ this.actionName = "CLOSE";
+ this.entityName = "SMSCAMPAIGN";
+ this.entityId = resourceId;
+ this.href = "/smscampaign/"+resourceId + "?command=close";
+ return this;
+ }
+ public CommandWrapperBuilder reactivateSmsCampaign(final Long resourceId) {
+ this.actionName = "REACTIVATE";
+ this.entityName = "SMSCAMPAIGN";
+ this.entityId = resourceId;
+ this.href = "/smscampaign/"+resourceId + "?command=reactivate";
+ return this;
+ }
+
+ public CommandWrapperBuilder deleteSmsCampaign(final Long resourceId) {
+ this.actionName = "DELETE";
+ this.entityName = "SMSCAMPAIGN";
+ this.entityId = resourceId;
+ this.href = "/smscampaign/"+resourceId;
+ return this;
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java
new file mode 100644
index 000000000..b6a0aaf7c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java
@@ -0,0 +1,76 @@
+/**
+ * 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.constants;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
+public enum CampaignType {
+ INVALID(0, "campaignType.invalid"), SMS(1, "campaignType.sms");
+
+ private Integer value;
+ private String code;
+
+ private CampaignType(Integer value, String code) {
+ this.value = value;
+ this.code = code;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public static CampaignType fromInt(final Integer typeValue) {
+ CampaignType type = null;
+ switch (typeValue) {
+ case 0:
+ type = INVALID;
+ break;
+ case 1:
+ type = SMS;
+ break;
+ }
+ return type;
+ }
+
+ public static EnumOptionData campaignType(final Integer campaignTypeId) {
+ return campaignType(CampaignType.fromInt(campaignTypeId));
+ }
+
+ public static EnumOptionData campaignType(final CampaignType campaignType) {
+ EnumOptionData optionData = new EnumOptionData(CampaignType.INVALID.getValue().longValue(), CampaignType.INVALID.getCode(),
+ "Invalid");
+ switch (campaignType) {
+ case INVALID:
+ optionData = new EnumOptionData(CampaignType.INVALID.getValue().longValue(), CampaignType.INVALID.getCode(), "Invalid");
+ break;
+ case SMS:
+ optionData = new EnumOptionData(CampaignType.SMS.getValue().longValue(), CampaignType.SMS.getCode(), "SMS");
+ break;
+ }
+ return optionData;
+ }
+
+ public boolean isSms() {
+ return this.value.equals(CampaignType.SMS.getValue());
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/helper/SmsConfigUtils.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/helper/SmsConfigUtils.java
new file mode 100644
index 000000000..a7fc94264
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/helper/SmsConfigUtils.java
@@ -0,0 +1,76 @@
+/**
+ * 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.helper;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignConstants;
+import org.apache.fineract.infrastructure.campaigns.sms.data.MessageGatewayConfigurationData;
+import org.apache.fineract.infrastructure.configuration.service.ExternalServicesPropertiesReadPlatformService;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SmsConfigUtils {
+
+ @Autowired
+ private ExternalServicesPropertiesReadPlatformService propertiesReadPlatformService;
+
+ //This method will return uri and HttpEntry objects with keys as uri and entity
+ public Map
getMessageGateWayRequestURI(final String apiEndPoint, String apiQueueResourceDatas) {
+ Map httpRequestdetails = new HashMap<>();
+ MessageGatewayConfigurationData messageGatewayConfigurationData = this.propertiesReadPlatformService.getSMSGateway();
+ final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.add(SmsCampaignConstants.FINERACT_PLATFORM_TENANT_ID, tenant.getTenantIdentifier());
+ headers.add(SmsCampaignConstants.FINERACT_TENANT_APP_KEY, messageGatewayConfigurationData.getTenantAppKey());
+ StringBuilder pathBuilder = new StringBuilder();
+ String endPoint = (messageGatewayConfigurationData.getEndPoint() == null || messageGatewayConfigurationData.getEndPoint().equals(
+ "/")) ? "" : messageGatewayConfigurationData.getEndPoint();
+ pathBuilder = (messageGatewayConfigurationData.getEndPoint() == null || messageGatewayConfigurationData.getEndPoint().equals("/")) ? pathBuilder
+ .append("{apiEndPoint}") : pathBuilder.append("{endPoint}/{apiEndPoint}");
+ // pathBuilder.append("{endPoint}/{apiEndPoint}") ;
+ UriBuilder builder = UriBuilder.fromPath(pathBuilder.toString()).host(messageGatewayConfigurationData.getHostName()).scheme("http")
+ .port(messageGatewayConfigurationData.getPortNumber());
+ URI uri = (messageGatewayConfigurationData.getEndPoint() == null || messageGatewayConfigurationData.getEndPoint().equals("/")) ? builder
+ .build(apiEndPoint) : builder.build(endPoint, apiEndPoint);
+ HttpEntity> entity = null;
+ if (apiQueueResourceDatas != null) {
+ entity = new HttpEntity<>(apiQueueResourceDatas, headers);
+ } else {
+ entity = new HttpEntity<>(headers);
+ }
+
+ httpRequestdetails.put("uri", uri);
+ httpRequestdetails.put("entity", entity);
+
+ return httpRequestdetails;
+ }
+
+}
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
new file mode 100644
index 000000000..5c7dd7b15
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/api/SmsCampaignApiResource.java
@@ -0,0 +1,195 @@
+/**
+ * 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.sms.api;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+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.campaigns.constants.CampaignType;
+import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignConstants;
+import org.apache.fineract.infrastructure.campaigns.sms.data.CampaignPreviewData;
+import org.apache.fineract.infrastructure.campaigns.sms.data.SmsCampaignData;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignReadPlatformService;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+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.security.service.PlatformSecurityContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+
+@Path("/smscampaigns")
+@Component
+@Scope("singleton")
+public class SmsCampaignApiResource {
+
+ private final PlatformSecurityContext platformSecurityContext;
+ private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
+ private final DefaultToApiJsonSerializer toApiJsonSerializer;
+ private final SmsCampaignReadPlatformService smsCampaignReadPlatformService;
+ private final ApiRequestParameterHelper apiRequestParameterHelper;
+ private final FromJsonHelper fromJsonHelper;
+ private final DefaultToApiJsonSerializer previewCampaignMessageDefaultToApiJsonSerializer;
+ private final SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ private final String resourceNameForPermissions = "SMS_CAMPAIGN";
+ private final PlatformSecurityContext context;
+
+ @Autowired
+ public SmsCampaignApiResource(final PlatformSecurityContext platformSecurityContext,
+ final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
+ final DefaultToApiJsonSerializer toApiJsonSerializer,
+ final SmsCampaignReadPlatformService smsCampaignReadPlatformService, final ApiRequestParameterHelper apiRequestParameterHelper,
+ final FromJsonHelper fromJsonHelper,
+ final DefaultToApiJsonSerializer previewCampaignMessageDefaultToApiJsonSerializer,
+ final PlatformSecurityContext context, final SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.platformSecurityContext = platformSecurityContext;
+ this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
+ this.toApiJsonSerializer = toApiJsonSerializer;
+ this.smsCampaignReadPlatformService = smsCampaignReadPlatformService;
+ this.apiRequestParameterHelper = apiRequestParameterHelper;
+ this.fromJsonHelper = fromJsonHelper;
+ this.previewCampaignMessageDefaultToApiJsonSerializer = previewCampaignMessageDefaultToApiJsonSerializer;
+ this.context = context;
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+
+ @GET
+ @Path("template")
+ public String template(@Context final UriInfo uriInfo) {
+ this.platformSecurityContext.authenticatedUser().validateHasReadPermission(SmsCampaignConstants.RESOURCE_NAME);
+ final SmsCampaignData smsCampaignData = this.smsCampaignReadPlatformService.retrieveTemplate(CampaignType.SMS.name());
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, smsCampaignData);
+ }
+
+ @POST
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String createCampaign(final String apiRequestBodyAsJson) {
+ this.platformSecurityContext.authenticatedUser();
+ final CommandWrapper commandRequest = new CommandWrapperBuilder().createSmsCampaign().withJson(apiRequestBodyAsJson).build();
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
+ @GET
+ @Path("{resourceId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String retrieveCampaign(@PathParam("resourceId") final Long resourceId, @Context final UriInfo uriInfo) {
+ this.platformSecurityContext.authenticatedUser().validateHasReadPermission(SmsCampaignConstants.RESOURCE_NAME);
+ SmsCampaignData smsCampaignData = this.smsCampaignReadPlatformService.retrieveOne(resourceId);
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, smsCampaignData);
+
+ }
+
+ @GET
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String retrieveAllCampaigns(@Context final UriInfo uriInfo) {
+ this.platformSecurityContext.authenticatedUser().validateHasReadPermission(SmsCampaignConstants.RESOURCE_NAME);
+ Collection smsCampaignDataCollection = this.smsCampaignReadPlatformService.retrieveAll();
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.toApiJsonSerializer.serialize(settings, smsCampaignDataCollection);
+ }
+
+ @PUT
+ @Path("{campaignId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String updateCampaign(@PathParam("campaignId") final Long campaignId, final String apiRequestBodyAsJson) {
+ final CommandWrapper commandRequest = new CommandWrapperBuilder().updateSmsCampaign(campaignId).withJson(apiRequestBodyAsJson)
+ .build();
+ final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return this.toApiJsonSerializer.serialize(result);
+ }
+
+ @POST
+ @Path("{campaignId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String handleCommands(@PathParam("campaignId") 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.activateSmsCampaign(campaignId).build();
+ result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ } else if (is(commandParam, "close")) {
+ commandRequest = builder.closeSmsCampaign(campaignId).build();
+ result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ } else if (is(commandParam, "reactivate")) {
+ commandRequest = builder.reactivateSmsCampaign(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);
+
+ CampaignPreviewData campaignMessage = null;
+ final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
+ final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
+ campaignMessage = this.smsCampaignWritePlatformService.previewMessage(query);
+ final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return this.previewCampaignMessageDefaultToApiJsonSerializer.serialize(settings, campaignMessage, new HashSet());
+
+ }
+
+ @DELETE
+ @Path("{campaignId}")
+ public String delete(@PathParam("campaignId") final Long campaignId) {
+ final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteSmsCampaign(campaignId).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/sms/constants/SmsCampaignConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignConstants.java
new file mode 100644
index 000000000..104f4c73e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignConstants.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.sms.constants;
+
+public interface SmsCampaignConstants {
+
+ String SMS_CAMPAIGN_NAME = "sms";
+
+ String RESOURCE_NAME = "SMSCAMPAIGN";
+
+ String SMS_BRIDGE = "sms_bridge";
+
+ String SMS = "sms";
+
+ String FINERACT_PLATFORM_TENANT_ID = "Fineract-Platform-TenantId";
+
+ String FINERACT_TENANT_APP_KEY = "Fineract-Tenant-App-Key";
+
+ //Supported Triggered Campaigns
+
+ String LOAN_REJECT = "Loan Rejected" ;
+
+ String LOAN_APPROVED = "Loan Approved" ;
+
+ String LOAN_REPAYMENT = "Loan Repayment" ;
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java
new file mode 100644
index 000000000..16ae3cd24
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.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.sms.constants;
+
+import java.time.Month;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.campaigns.constants.CampaignType;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
+
+
+public class SmsCampaignEnumerations {
+
+
+ public static EnumOptionData smscampaignTriggerType(final SmsCampaignTriggerType type) {
+ EnumOptionData optionData = new EnumOptionData(SmsCampaignTriggerType.INVALID.getValue().longValue(),
+ SmsCampaignTriggerType.INVALID.getCode(), "Invalid");
+ switch (type) {
+ case INVALID:
+ break;
+ case DIRECT:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.DIRECT.getValue().longValue(),
+ SmsCampaignTriggerType.DIRECT.getCode(), "Direct");
+ break;
+ case SCHEDULE:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.SCHEDULE.getValue().longValue(),
+ SmsCampaignTriggerType.SCHEDULE.getCode(), "Scheduled");
+ break;
+ case TRIGGERED:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.TRIGGERED.getValue().longValue(),
+ SmsCampaignTriggerType.TRIGGERED.getCode(), "Triggered");
+ break;
+ }
+ return optionData;
+ }
+
+ public static EnumOptionData smscampaignType(final CampaignType type) {
+ EnumOptionData optionData = new EnumOptionData(CampaignType.INVALID.getValue().longValue(), CampaignType.INVALID.getCode(),
+ "Invalid");
+ switch (type) {
+ case INVALID:
+ break;
+ case SMS:
+ optionData = new EnumOptionData(CampaignType.SMS.getValue().longValue(), CampaignType.SMS.getCode(), "SMS");
+ break;
+ }
+ return optionData;
+ }
+
+ public static EnumOptionData calendarMonthType(final Month entityType) {
+ final EnumOptionData optionData = new EnumOptionData(new Long(entityType.getValue()), entityType.name(), entityType.name());
+ return optionData;
+ }
+
+ public static List calendarMonthType() {
+ final List optionDatas = new ArrayList<>();
+ for (final Month monthType : Month.values()) {
+ if (Month.DECEMBER.compareTo(monthType) != 0) {
+ optionDatas.add(calendarMonthType(monthType));
+ }
+ }
+ return optionDatas;
+ }
+
+ public static List calendarPeriodFrequencyTypes(final PeriodFrequencyType[] periodFrequencyTypes) {
+ final List optionDatas = new ArrayList<>();
+ for (final PeriodFrequencyType periodFrequencyType : periodFrequencyTypes) {
+ final EnumOptionData optionData = new EnumOptionData(periodFrequencyType.getValue().longValue(), periodFrequencyType.getCode(),
+ periodFrequencyType.toString());
+ optionDatas.add(optionData);
+ }
+ return optionDatas;
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignStatus.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignStatus.java
new file mode 100644
index 000000000..43d3c27d7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignStatus.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.sms.constants;
+
+public enum SmsCampaignStatus {
+
+ INVALID(-1, "smsCampaignStatus.invalid"), //
+ PENDING(100, "smsCampaignStatus.pending"), //
+ ACTIVE(300, "smsCampaignStatus.active"), //
+ CLOSED(600, "smsCampaignStatus.closed");
+
+ private final Integer value;
+ private final String code;
+
+ private SmsCampaignStatus(Integer value, String code) {
+ this.value = value;
+ this.code = code;
+ }
+
+ public static SmsCampaignStatus fromInt(final Integer statusValue) {
+
+ SmsCampaignStatus enumeration = SmsCampaignStatus.INVALID;
+ switch (statusValue) {
+ case 100:
+ enumeration = SmsCampaignStatus.PENDING;
+ break;
+ case 300:
+ enumeration = SmsCampaignStatus.ACTIVE;
+ break;
+ case 600:
+ enumeration = SmsCampaignStatus.CLOSED;
+ break;
+ }
+ return enumeration;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public boolean isActive() {
+ return this.value.equals(SmsCampaignStatus.ACTIVE.getValue());
+ }
+
+ public boolean isPending() {
+ return this.value.equals(SmsCampaignStatus.PENDING.getValue());
+ }
+
+ public boolean isClosed() {
+ return this.value.equals(SmsCampaignStatus.CLOSED.getValue());
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignTriggerType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignTriggerType.java
new file mode 100644
index 000000000..a133e6f9d
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignTriggerType.java
@@ -0,0 +1,98 @@
+/**
+ * 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.sms.constants;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
+public enum SmsCampaignTriggerType {
+ INVALID(-1, "triggerType.invalid"), DIRECT(1, "triggerType.direct"), SCHEDULE(2, "triggerType.schedule"), TRIGGERED(3,
+ "triggerType.triggered");
+
+ private Integer value;
+ private String code;
+
+ private SmsCampaignTriggerType(Integer value, String code) {
+ this.value = value;
+ this.code = code;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public static SmsCampaignTriggerType fromInt(final Integer typeValue) {
+ SmsCampaignTriggerType type = null;
+ switch (typeValue) {
+ case 1:
+ type = DIRECT;
+ break;
+ case 2:
+ type = SCHEDULE;
+ break;
+ case 3:
+ type = TRIGGERED;
+ break;
+ }
+ return type;
+ }
+
+ public static EnumOptionData triggerType(final Integer triggerTypeId) {
+ return triggerType(SmsCampaignTriggerType.fromInt(triggerTypeId));
+ }
+
+ public static EnumOptionData triggerType(final SmsCampaignTriggerType triggerType) {
+ EnumOptionData optionData = new EnumOptionData(SmsCampaignTriggerType.INVALID.getValue().longValue(),
+ SmsCampaignTriggerType.INVALID.getCode(), "Invalid");
+ switch (triggerType) {
+ case INVALID:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.INVALID.getValue().longValue(),
+ SmsCampaignTriggerType.INVALID.getCode(), "Invalid");
+ break;
+ case DIRECT:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.DIRECT.getValue().longValue(),
+ SmsCampaignTriggerType.DIRECT.getCode(), "Direct");
+ break;
+ case SCHEDULE:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.SCHEDULE.getValue().longValue(),
+ SmsCampaignTriggerType.SCHEDULE.getCode(), "Schedule");
+ break;
+ case TRIGGERED:
+ optionData = new EnumOptionData(SmsCampaignTriggerType.TRIGGERED.getValue().longValue(),
+ SmsCampaignTriggerType.TRIGGERED.getCode(), "Triggered");
+ break;
+ }
+ return optionData;
+ }
+
+ public boolean isDirect() {
+ return this.value.equals(SmsCampaignTriggerType.DIRECT.getValue());
+ }
+
+ public boolean isSchedule() {
+ return this.value.equals(SmsCampaignTriggerType.SCHEDULE.getValue());
+ }
+
+ public boolean isTriggered() {
+ return this.value.equals(SmsCampaignTriggerType.TRIGGERED.getValue());
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/CampaignPreviewData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/CampaignPreviewData.java
new file mode 100644
index 000000000..73ca41f73
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/CampaignPreviewData.java
@@ -0,0 +1,39 @@
+/**
+ * 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.sms.data;
+
+public class CampaignPreviewData {
+
+ private final String campaignMessage;
+
+ private final Integer totalNumberOfMessages;
+
+ public CampaignPreviewData(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/sms/data/CampaignTriggerWithSubTypes.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/CampaignTriggerWithSubTypes.java
new file mode 100644
index 000000000..c23771fc7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/CampaignTriggerWithSubTypes.java
@@ -0,0 +1,298 @@
+/**
+ * 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.sms.data;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
+public final class CampaignTriggerWithSubTypes {
+
+ public enum ActualCampaignTriggerType {
+ INVALID(0, "campaignTriggerType.invalid"), //
+ LOAN(1, "campaignTriggerType.loan"), //
+ SAVING(2, "campaignTriggerType.saving"), //
+ CLIENT(3, "campaignTriggerType.client");
+
+ private Integer value;
+ private String code;
+
+ private ActualCampaignTriggerType(Integer value, String code) {
+ this.value = value;
+ this.code = code;
+ }
+
+ public static ActualCampaignTriggerType fromInt(final Integer typeValue) {
+ ActualCampaignTriggerType type = ActualCampaignTriggerType.INVALID;
+ switch (typeValue) {
+ case 1:
+ type = LOAN;
+ break;
+ case 2:
+ type = SAVING;
+ break;
+ case 3:
+ type = CLIENT;
+ break;
+ }
+ return type;
+ }
+
+ public Integer getValue() {
+ return this.value;
+ }
+
+ public String getCode() {
+ return this.code;
+ }
+
+ public static EnumOptionData toEnumOptionData(final ActualCampaignTriggerType triggerType) {
+ final EnumOptionData optionData = new EnumOptionData(new Long(triggerType.getValue()), triggerType.getCode(), triggerType.name());
+ return optionData;
+ }
+
+ public static EnumOptionData toEnumOptionData(final Integer triggerTypeValue) {
+ ActualCampaignTriggerType actualCampaignTriggerType = ActualCampaignTriggerType.fromInt(triggerTypeValue);
+ final EnumOptionData optionData = new EnumOptionData(new Long(actualCampaignTriggerType.getValue()),
+ actualCampaignTriggerType.getCode(), actualCampaignTriggerType.name());
+ return optionData;
+ }
+
+ public boolean isInvalid() {
+ return this.value.equals(ActualCampaignTriggerType.INVALID.getValue());
+ }
+ }
+
+ public enum CampaignTriggerSubType {
+ INVALID(0, ActualCampaignTriggerType.INVALID, "campaignTriggerSubType.invalid"), //
+
+ DISBURSE(101, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.disburse"), //
+ REPAYMENT(102, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.repayment"), //
+ UNDO_DISBURSAL(103, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.undodisbursal"), //
+ WRITE_OFF(104, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.writeoff"), //
+ ADJUST(105, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.adjust"), //
+ UNDO_WRITE_OFF(106, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.undowriteoff"), //
+ FORECLOSURE(107, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.foreclosure"), //
+ APPROVED(108, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.approved"), //
+ WAIVE_INTEREST(109, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.waive.interest"), //
+ CLOSE(110, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.close"), //
+ CLOSE_AS_RESCHEDULE(111, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.close.as.rescheduled"), //
+ ADD_CHARGE(112, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.add.charge"), //
+ UPDATE_CHARGE(113, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.update.charge"), //
+ WAIVE_CHARGE(114, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.waive.charge"), //
+ DELETE_CHARGE(115, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.delete.charge"), //
+ CHARGE_PAYMENT(116, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.charge.payment"), //
+ INITIATE_TRANSFER(117, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.initiate.transfer"), //
+ ACCEPT_TRANSFER(118, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.accept.transfer"), //
+ WITHDRAW_TRANSFER(119, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.withdraw.transfer"), //
+ REJECT_TRANSFER(120, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.reject.transfer"), //
+ REASSIGN_OFFICER(121, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.reassign.officer"), //
+ REMOVE_OFFICER(122, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.reassign.officer"), //
+ APPLY_OVERDUE_CHARGE(123, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.reassign.officer"), //
+ INTEREST_RECALCULATION(124, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.reassign.officer"), //
+ REFUND(125, ActualCampaignTriggerType.LOAN, "campaignTriggerSubType.reassign.officer"), //
+
+ SAVINGS_DEPOSIT(201, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.deposit"), //
+ SAVINGS_WITHDRAWAL(202, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.withdrawal"), //
+ SAVINGS_ACTIVATE(203, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.activate"), //
+ SAVINGS_ADJUST_TRANSACTION(204, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.adjust"), //
+ SAVINGS_APPLY_ANNUAL_FEE(205, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.apply.annual.fee"), //
+ SAVINGS_CALCULATE_INTEREST(206, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.calclulate.interest"), //
+ SAVINGS_CLOSE(207, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.close"), //
+ SAVINGS_POST_INTEREST(208, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.post"), //
+ SAVINGS_REJECT(209, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.reject"), //
+ SAVINGS_UNDO(210, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.undo"), //
+ SAVINGS_ADD_CHARGE(211, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.add.charge"), //
+ SAVINGS_WAIVE_CHARGE(212, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.waive.charge"), //
+ SAVINGS_PAY_CHARGE(213, ActualCampaignTriggerType.SAVING, "campaignTriggerSubType.savings.pay.charge"), //
+
+ CLIENTS_ACTIVATE(301, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.activate"), //
+ CLIENTS_CLOSE(302, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.close"), //
+ CLIENTS_ACCEPT_TRANSFER(303, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.accept.transfer"), //
+ CLIENTS_ASSIGN_STAFF(304, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.assign.staff"), //
+ CLIENTS_CREATE(305, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.create"), //
+ CLIENTS_DELETE(306, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.delete"), //
+ CLIENTS_PROPOSE_TRANSFER(307, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.propose.transfer"), //
+ CLIENTS_REACTIVATE(308, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.reactivate"), //
+ CLIENTS_REJECT(309, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.reject"), //
+ CLIENTS_REJECT_TRANSFER(310, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.reject.transfer"), //
+ CLIENTS_WITHDRAW(311, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.withdraw"), //
+ CLIENTS_WITHDRAW_TRANSFER(312, ActualCampaignTriggerType.CLIENT, "campaignTriggerSubType.withdraw.transfer");
+
+ private Integer id;
+ private ActualCampaignTriggerType type;
+ private String code;
+
+ private CampaignTriggerSubType(Integer id, ActualCampaignTriggerType type, String code) {
+ this.id = id;
+ this.type = type;
+ this.code = code;
+ }
+
+ public static CampaignTriggerSubType fromInt(final Integer subTypeValue) {
+ CampaignTriggerSubType subType = CampaignTriggerSubType.INVALID;
+ switch (subTypeValue) {
+ case 101:
+ subType = DISBURSE;
+ break;
+ case 102:
+ subType = REPAYMENT;
+ break;
+ case 103:
+ subType = ADJUST;
+ break;
+ case 104:
+ subType = UNDO_DISBURSAL;
+ break;
+ case 105:
+ subType = WRITE_OFF;
+ break;
+ case 106:
+ subType = UNDO_WRITE_OFF;
+ break;
+
+ case 201:
+ subType = SAVINGS_DEPOSIT;
+ break;
+ case 202:
+ subType = SAVINGS_WITHDRAWAL;
+ break;
+ case 203:
+ subType = SAVINGS_ACTIVATE;
+ break;
+ case 204:
+ subType = SAVINGS_ADJUST_TRANSACTION;
+ break;
+ case 205:
+ subType = SAVINGS_APPLY_ANNUAL_FEE;
+ break;
+ case 206:
+ subType = SAVINGS_CALCULATE_INTEREST;
+ break;
+ case 207:
+ subType = SAVINGS_CLOSE;
+ break;
+ case 208:
+ subType = SAVINGS_POST_INTEREST;
+ break;
+ case 209:
+ subType = SAVINGS_REJECT;
+ break;
+ case 210:
+ subType = SAVINGS_UNDO;
+ break;
+ case 211:
+ subType = SAVINGS_ADD_CHARGE;
+ break;
+ case 212:
+ subType = SAVINGS_WAIVE_CHARGE;
+ break;
+ case 213:
+ subType = SAVINGS_PAY_CHARGE;
+ break;
+
+ case 301:
+ subType = CLIENTS_ACTIVATE;
+ break;
+ case 302:
+ subType = CLIENTS_CLOSE;
+ break;
+ case 303:
+ subType = CLIENTS_ACCEPT_TRANSFER;
+ break;
+ case 304:
+ subType = CLIENTS_ASSIGN_STAFF;
+ break;
+ case 305:
+ subType = CLIENTS_CREATE;
+ break;
+ case 306:
+ subType = CLIENTS_DELETE;
+ break;
+ case 307:
+ subType = CLIENTS_PROPOSE_TRANSFER;
+ break;
+ case 308:
+ subType = CLIENTS_REACTIVATE;
+ break;
+ case 309:
+ subType = CLIENTS_REJECT;
+ break;
+ case 310:
+ subType = CLIENTS_REJECT_TRANSFER;
+ break;
+ case 311:
+ subType = CLIENTS_WITHDRAW;
+ break;
+ case 312:
+ subType = CLIENTS_WITHDRAW_TRANSFER;
+ break;
+ }
+ return subType;
+ }
+
+ public Integer getId() {
+ return this.id;
+ }
+
+ public ActualCampaignTriggerType getType() {
+ return this.type;
+ }
+
+ public String getCode() {
+ return this.code;
+ }
+
+ public static EnumOptionData toEnumOptionData(final Integer triggerSubType) {
+ CampaignTriggerSubType subTypeEnum = CampaignTriggerSubType.fromInt(triggerSubType);
+ final EnumOptionData optionData = new EnumOptionData(new Long(subTypeEnum.getId()), subTypeEnum.getCode(), subTypeEnum.name());
+ return optionData;
+ }
+ }
+
+ public static List addTypeSubTypeMapping(ActualCampaignTriggerType type) {
+ List subTypeList = new ArrayList<>();
+ EnumOptionData optionData = null;
+ for (CampaignTriggerSubType subType : CampaignTriggerSubType.values()) {
+ if (subType.getType().equals(type)) {
+ optionData = new EnumOptionData(subType.getId().longValue(), subType.getCode(), subType.name());
+ subTypeList.add(optionData);
+ }
+ }
+ return subTypeList;
+ }
+
+ public static Collection getTriggerTypeAndSubTypes() {
+ final Collection typesList = new ArrayList<>();
+ EnumOptionData actualTriggerType = null;
+ for (ActualCampaignTriggerType triggerType : ActualCampaignTriggerType.values()) {
+ if (triggerType.isInvalid()) {
+ continue;
+ }
+ List subTypeList = addTypeSubTypeMapping(triggerType);
+ actualTriggerType = ActualCampaignTriggerType.toEnumOptionData(triggerType);
+ TriggerTypeWithSubTypesData triggerTypeWithSubTypesData = new TriggerTypeWithSubTypesData(actualTriggerType, subTypeList);
+ typesList.add(triggerTypeWithSubTypesData);
+ }
+ return typesList;
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/MessageGatewayConfigurationData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/MessageGatewayConfigurationData.java
new file mode 100644
index 000000000..f2222ee64
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/MessageGatewayConfigurationData.java
@@ -0,0 +1,81 @@
+/**
+ * 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.sms.data;
+
+public class MessageGatewayConfigurationData {
+
+ private final Long id;
+ private final String connectionName;
+ private final String hostName;
+ private final int portNumber;
+ private final String endPoint;
+ private final String userName;
+ private final String password;
+ private final boolean sslEnabled;
+ private final String tenantAppKey;
+
+ public MessageGatewayConfigurationData(final Long id, final String connectionName, final String hostName, final int portNumber,
+ final String endPoint, final String userName, final String password, final boolean sslEnabled, final String tenantAppKey) {
+ this.id = id;
+ this.connectionName = connectionName;
+ this.hostName = hostName;
+ this.portNumber = portNumber;
+ this.endPoint = endPoint;
+ this.userName = userName;
+ this.password = password;
+ this.sslEnabled = sslEnabled;
+ this.tenantAppKey = tenantAppKey;
+ }
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public String getConnectionName() {
+ return this.connectionName;
+ }
+
+ public String getHostName() {
+ return this.hostName;
+ }
+
+ public int getPortNumber() {
+ return this.portNumber;
+ }
+
+ public String getEndPoint() {
+ return this.endPoint;
+ }
+
+ public String getUserName() {
+ return this.userName;
+ }
+
+ public String getPassword() {
+ return this.password;
+ }
+
+ public boolean isSslEnabled() {
+ return this.sslEnabled;
+ }
+
+ public String getTenantAppKey() {
+ return this.tenantAppKey;
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsBusinessRulesData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsBusinessRulesData.java
new file mode 100644
index 000000000..adee0940c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsBusinessRulesData.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.sms.data;
+
+import java.util.Map;
+
+public class SmsBusinessRulesData {
+
+ private final Long reportId;
+
+ private final String reportName;
+
+ private final String reportType;
+
+ private final String reportSubType;
+
+ private final String reportDescription;
+
+ private final Map reportParamName;
+
+ public SmsBusinessRulesData(final Long reportId, final String reportName, final String reportType, final String reportSubType,
+ final Map reportParamName, final String reportDescription) {
+ this.reportId = reportId;
+ this.reportName = reportName;
+ this.reportType = reportType;
+ this.reportSubType = reportSubType;
+ this.reportParamName = reportParamName;
+ this.reportDescription = reportDescription;
+ }
+
+ public static SmsBusinessRulesData instance(final Long reportId, final String reportName, final String reportType,
+ final String reportSubType, final Map reportParamName, final String reportDescription) {
+ return new SmsBusinessRulesData(reportId, reportName, reportType, reportSubType, reportParamName, reportDescription);
+ }
+
+ public Map getReportParamName() {
+ return reportParamName;
+ }
+
+ public String getReportType() {
+ return reportType;
+ }
+
+ public String getReportSubType() {
+ return this.reportSubType;
+ }
+
+ 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;
+
+ SmsBusinessRulesData that = (SmsBusinessRulesData) o;
+
+ if (reportId != null ? !reportId.equals(that.reportId) : that.reportId != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return reportId != null ? reportId.hashCode() : 0;
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java
new file mode 100644
index 000000000..8a6e6a310
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java
@@ -0,0 +1,206 @@
+/**
+ * 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.sms.data;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+@SuppressWarnings("unused")
+public class SmsCampaignData {
+
+ private Long id;
+ private final String campaignName;
+ private final EnumOptionData campaignType;
+ private final Long runReportId;
+ private final String reportName;
+ private final String paramValue;
+ private final EnumOptionData campaignStatus;
+ private final EnumOptionData triggerType;
+ private final String campaignMessage;
+ private final DateTime nextTriggerDate;
+ private final LocalDate lastTriggerDate;
+ private final SmsCampaignTimeLine smsCampaignTimeLine;
+ private final DateTime recurrenceStartDate;
+ private final String recurrence;
+ private final Long providerId;
+
+ private final Collection smsProviderOptions;
+
+ private final Collection campaignTypeOptions;
+
+ private final Collection triggerTypeOptions;
+
+ private final Collection businessRulesOptions;
+
+ private final Collection months;
+
+ private final Collection weekDays;
+
+ private final Collection frequencyTypeOptions;
+
+ private final Collection periodFrequencyOptions;
+
+ private SmsCampaignData(final Long id, final String campaignName, final EnumOptionData campaignType, final EnumOptionData triggerType,
+ final Long runReportId,
+ final String reportName, final String paramValue, final EnumOptionData campaignStatus,
+ final String message, final DateTime nextTriggerDate, final LocalDate lastTriggerDate,
+ final SmsCampaignTimeLine smsCampaignTimeLine, final DateTime recurrenceStartDate, final String recurrence,
+ final Long providerId, final Collection businessRulesOptions,
+ final Collection smsProviderOptions, final Collection campaignTypeOptions,
+ final Collection triggerTypeOptions, final Collection months,
+ final Collection weekDays, final Collection frequencyTypeOptions,
+ final Collection periodFrequencyOptions) {
+ this.id = id;
+ this.campaignName = campaignName;
+ this.campaignType = campaignType;
+ this.triggerType = triggerType;
+ this.runReportId = runReportId;
+ this.reportName = reportName;
+ this.paramValue = paramValue;
+ this.campaignStatus = campaignStatus;
+ this.campaignMessage = message;
+ if (nextTriggerDate != null) {
+ this.nextTriggerDate = nextTriggerDate;
+ } else {
+ this.nextTriggerDate = null;
+ }
+ if (lastTriggerDate != null) {
+ this.lastTriggerDate = lastTriggerDate;
+ } else {
+ this.lastTriggerDate = null;
+ }
+ this.smsCampaignTimeLine = smsCampaignTimeLine;
+ this.recurrenceStartDate = recurrenceStartDate;
+ this.recurrence = recurrence;
+ this.providerId = providerId;
+ this.businessRulesOptions = businessRulesOptions;
+ this.smsProviderOptions = smsProviderOptions;
+ this.campaignTypeOptions = campaignTypeOptions;
+ this.triggerTypeOptions = triggerTypeOptions;
+ this.months = months;
+ this.weekDays = weekDays;
+ this.frequencyTypeOptions = frequencyTypeOptions;
+ this.periodFrequencyOptions = periodFrequencyOptions;
+ }
+
+ public static SmsCampaignData instance(final Long id, final String campaignName, final EnumOptionData campaignType,
+ final EnumOptionData triggerType,
+ final Long runReportId, final String reportName, final String paramValue, final EnumOptionData campaignStatus,
+ final String message, final DateTime nextTriggerDate, final LocalDate lastTriggerDate,
+ final SmsCampaignTimeLine smsCampaignTimeLine, final DateTime recurrenceStartDate, final String recurrence,
+ final Long providerId) {
+ final Collection businessRulesOptions = null;
+ final Collection smsProviderOptions = null;
+ final Collection campaignTypeOptions = null;
+ final Collection triggerTypeOptions = null;
+ final Collection months = null;
+ final Collection weekDays = null;
+ final Collection frequencyTypeOptions = null;
+ final Collection periodFrequencyOptions = null;
+
+ return new SmsCampaignData(id, campaignName, campaignType, triggerType, runReportId,
+ reportName, paramValue, campaignStatus, message, nextTriggerDate, lastTriggerDate, smsCampaignTimeLine,
+ recurrenceStartDate, recurrence, providerId, businessRulesOptions, smsProviderOptions, campaignTypeOptions,
+ triggerTypeOptions, months, weekDays, frequencyTypeOptions, periodFrequencyOptions);
+ }
+
+ public static SmsCampaignData template(final Collection smsProviderOptions,
+ final Collection campaignTypeOptions, final Collection businessRulesOptions,
+ final Collection triggerTypeOptions, final Collection months,
+ final Collection weekDays, final Collection frequencyTypeOptions,
+ final Collection periodFrequencyOptions) {
+ final Long id = null;
+ final String campaignName = null;
+ final EnumOptionData campaignType = null;
+ final Long runReportId = null;
+ final String paramValue = null;
+ final EnumOptionData campaignStatus = null;
+ final String message = null;
+ final DateTime nextTriggerDate = null;
+ final LocalDate lastTriggerDate = null;
+ final SmsCampaignTimeLine smsCampaignTimeLine = null;
+ final DateTime recurrenceStartDate = null;
+ final String recurrence = null;
+ final EnumOptionData triggerType = null;
+ final String reportName = null;
+ final Long providerId = null;
+ return new SmsCampaignData(id, campaignName, campaignType, triggerType, runReportId,
+ reportName, paramValue, campaignStatus, message, nextTriggerDate, lastTriggerDate, smsCampaignTimeLine,
+ recurrenceStartDate, recurrence, providerId, businessRulesOptions, smsProviderOptions, campaignTypeOptions,
+ triggerTypeOptions, months, weekDays, frequencyTypeOptions, periodFrequencyOptions);
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getCampaignName() {
+ return this.campaignName;
+ }
+
+ public EnumOptionData getCampaignType() {
+ return this.campaignType;
+ }
+
+ public Long getRunReportId() {
+ return this.runReportId;
+ }
+
+ public String getParamValue() {
+ return this.paramValue;
+ }
+
+ public EnumOptionData getCampaignStatus() {
+ return this.campaignStatus;
+ }
+
+ public String getMessage() {
+ return this.campaignMessage;
+ }
+
+ public DateTime getNextTriggerDate() {
+ return this.nextTriggerDate;
+ }
+
+ public LocalDate getLastTriggerDate() {
+ return this.lastTriggerDate;
+ }
+
+ public String getRecurrence() {
+ return this.recurrence;
+ }
+
+ public DateTime getRecurrenceStartDate() {
+ return this.recurrenceStartDate;
+ }
+
+ public String getReportName() {
+ return this.reportName;
+ }
+
+ public Long providerId() {
+ return this.providerId;
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignTimeLine.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignTimeLine.java
new file mode 100644
index 000000000..faf60735e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignTimeLine.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.sms.data;
+
+import org.joda.time.LocalDate;
+
+@SuppressWarnings("unused")
+public class SmsCampaignTimeLine {
+
+ 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 SmsCampaignTimeLine(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/sms/data/SmsProviderData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsProviderData.java
new file mode 100644
index 000000000..9ae976890
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsProviderData.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.sms.data;
+
+public class SmsProviderData {
+
+ private Long id;
+
+ private String tenantId;
+
+ private String phoneNo;
+
+ private String providerAppKey;
+
+ private String providerName;
+
+ private String providerDescription;
+
+ public SmsProviderData(final Long id, final String providerAppKey, final String providerName, final String providerDescription, final String tenantId,
+ final String phoneNo) {
+ this.id = id;
+ this.providerAppKey = providerAppKey;
+ this.providerName = providerName;
+ this.providerDescription = providerDescription;
+ this.tenantId = tenantId;
+ this.phoneNo = phoneNo;
+ }
+
+ public SmsProviderData() {
+
+ }
+
+ public Long getId() {
+ return this.id;
+ }
+
+ public String getProviderAppKey() {
+ return this.providerAppKey;
+ }
+
+ public String getProviderName() {
+ return this.providerName;
+ }
+
+ public String getProviderDescription() {
+ return this.providerDescription;
+ }
+
+ public String getTenantId() {
+ return this.tenantId;
+ }
+
+ public String getPhoneNo() {
+ return this.phoneNo;
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/TriggerTypeWithSubTypesData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/TriggerTypeWithSubTypesData.java
new file mode 100644
index 000000000..b6e9c3c6a
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/TriggerTypeWithSubTypesData.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.sms.data;
+
+import java.util.List;
+
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
+public class TriggerTypeWithSubTypesData {
+
+ private final EnumOptionData actualTriggerType;
+ private final List triggerSubTypes;
+
+ public TriggerTypeWithSubTypesData(final EnumOptionData actualTriggerType, List triggerSubTypes) {
+ this.actualTriggerType = actualTriggerType;
+ this.triggerSubTypes = triggerSubTypes;
+ }
+
+ public EnumOptionData getActualTriggerType() {
+ return this.actualTriggerType;
+ }
+
+ public List getTriggerSubTypes() {
+ return this.triggerSubTypes;
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java
new file mode 100644
index 000000000..3e1610e89
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java
@@ -0,0 +1,561 @@
+/**
+ * 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.sms.domain;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import javax.persistence.UniqueConstraint;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.campaigns.constants.CampaignType;
+import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignStatus;
+import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
+import org.apache.fineract.infrastructure.campaigns.sms.serialization.SmsCampaignValidator;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.ApiParameterError;
+import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+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.portfolio.calendar.domain.CalendarFrequencyType;
+import org.apache.fineract.portfolio.calendar.domain.CalendarWeekDaysType;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+@Entity
+@Table(name = "sms_campaign", uniqueConstraints = {@UniqueConstraint(columnNames = { "campaign_name" }, name = "campaign_name_UNIQUE")})
+public class SmsCampaign extends AbstractPersistableCustom {
+
+ @Column(name = "campaign_name", nullable = false)
+ private String campaignName;
+
+ @Column(name = "campaign_type", nullable = false)
+ private Integer campaignType ; //defines email or sms, etc..
+
+ @Column(name = "campaign_trigger_type", nullable = false)
+ private Integer triggerType; //defines direct, scheduled, transaction
+
+ @Column(name = "provider_id", nullable = false)
+ private Long providerId; // defined provider details
+
+ @ManyToOne
+ @JoinColumn(name = "report_id", nullable = false)
+ private Report businessRuleId;
+
+ @Column(name = "param_value")
+ private String paramValue;
+
+ @Column(name = "status_enum", nullable = false)
+ private Integer status;
+
+ @Column(name = "message", nullable = false)
+ private String message;
+
+ @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 = true)
+ private String recurrence;
+
+ @Column(name = "next_trigger_date", nullable = true)
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date nextTriggerDate;
+
+ @Column(name = "last_trigger_date", nullable = true)
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date lastTriggerDate;
+
+ @Column(name = "recurrence_start_date", nullable = true)
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date recurrenceStartDate;
+
+ @Column(name = "is_visible", nullable = true)
+ private boolean isVisible;
+
+ public SmsCampaign() {}
+
+ private SmsCampaign(final String campaignName, final Integer campaignType,
+ final Integer triggerType, final Report businessRuleId, final Long providerId, final String paramValue,
+ final String message, final LocalDate submittedOnDate, final AppUser submittedBy, final String recurrence,
+ final LocalDateTime localDateTime) {
+ this.campaignName = campaignName;
+ this.campaignType = campaignType;
+ this.triggerType = SmsCampaignTriggerType.fromInt(triggerType).getValue();
+ this.businessRuleId = businessRuleId;
+ this.providerId = providerId;
+ this.paramValue = paramValue;
+ this.status = SmsCampaignStatus.PENDING.getValue();
+ this.message = message;
+ 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 SmsCampaign instance(final AppUser submittedBy, final Report report, final JsonCommand command) {
+
+ final String campaignName = command.stringValueOfParameterNamed(SmsCampaignValidator.campaignName);
+ final Long campaignType = command.longValueOfParameterNamed(SmsCampaignValidator.campaignType);
+ final Long triggerType = command.longValueOfParameterNamed(SmsCampaignValidator.triggerType);
+ final Long providerId = command.longValueOfParameterNamed(SmsCampaignValidator.providerId);
+ final String paramValue = command.jsonFragment(SmsCampaignValidator.paramValue);
+
+ final String message = command.stringValueOfParameterNamed(SmsCampaignValidator.message);
+ LocalDate submittedOnDate = new LocalDate();
+ if (command.hasParameter(SmsCampaignValidator.submittedOnDateParamName)) {
+ submittedOnDate = command.localDateValueOfParameterNamed(SmsCampaignValidator.submittedOnDateParamName);
+ }
+ String recurrence = null;
+
+ LocalDateTime recurrenceStartDate = new LocalDateTime();
+ if (SmsCampaignTriggerType.fromInt(triggerType.intValue()).isSchedule()) {
+ final Locale locale = command.extractLocale();
+ String dateTimeFormat = null;
+ if (command.hasParameter(SmsCampaignValidator.dateTimeFormat)) {
+ dateTimeFormat = command.stringValueOfParameterNamed(SmsCampaignValidator.dateTimeFormat);
+ final DateTimeFormatter fmt = DateTimeFormat.forPattern(dateTimeFormat).withLocale(locale);
+ if (command.hasParameter(SmsCampaignValidator.recurrenceStartDate)) {
+ recurrenceStartDate = LocalDateTime.parse(
+ command.stringValueOfParameterNamed(SmsCampaignValidator.recurrenceStartDate), fmt);
+ }
+ recurrence = constructRecurrence(command);
+ }
+ } else {
+ recurrenceStartDate = null;
+ }
+
+ return new SmsCampaign(campaignName, campaignType.intValue(), triggerType.intValue(), report,
+ providerId, paramValue, message, submittedOnDate, submittedBy, recurrence, recurrenceStartDate);
+ }
+
+ public Map update(JsonCommand command) {
+
+ final Map actualChanges = new LinkedHashMap<>(5);
+
+ if (command.isChangeInStringParameterNamed(SmsCampaignValidator.campaignName, this.campaignName)) {
+ final String newValue = command.stringValueOfParameterNamed(SmsCampaignValidator.campaignName);
+ actualChanges.put(SmsCampaignValidator.campaignName, newValue);
+ this.campaignName = StringUtils.defaultIfEmpty(newValue, null);
+ }
+ if (command.isChangeInStringParameterNamed(SmsCampaignValidator.message, this.message)) {
+ final String newValue = command.stringValueOfParameterNamed(SmsCampaignValidator.message);
+ actualChanges.put(SmsCampaignValidator.message, newValue);
+ this.message = StringUtils.defaultIfEmpty(newValue, null);
+ }
+ if (command.isChangeInStringParameterNamed(SmsCampaignValidator.paramValue, this.paramValue)) {
+ final String newValue = command.jsonFragment(SmsCampaignValidator.paramValue);
+ actualChanges.put(SmsCampaignValidator.paramValue, newValue);
+ this.paramValue = StringUtils.defaultIfEmpty(newValue, null);
+ }
+ if (command.isChangeInIntegerParameterNamed(SmsCampaignValidator.campaignType, this.campaignType)) {
+ final Integer newValue = command.integerValueOfParameterNamed(SmsCampaignValidator.campaignType);
+ actualChanges.put(SmsCampaignValidator.campaignType, CampaignType.fromInt(newValue));
+ this.campaignType = CampaignType.fromInt(newValue).getValue();
+ }
+
+ if (command.isChangeInIntegerParameterNamed(SmsCampaignValidator.triggerType, this.triggerType)) {
+ final Integer newValue = command.integerValueOfParameterNamed(SmsCampaignValidator.triggerType);
+ actualChanges.put(SmsCampaignValidator.triggerType, SmsCampaignTriggerType.fromInt(newValue));
+ this.triggerType = SmsCampaignTriggerType.fromInt(newValue).getValue();
+ }
+
+ if (command.isChangeInLongParameterNamed(SmsCampaignValidator.runReportId,
+ (this.businessRuleId != null) ? this.businessRuleId.getId() : null)) {
+ final String newValue = command.stringValueOfParameterNamed(SmsCampaignValidator.runReportId);
+ actualChanges.put(SmsCampaignValidator.runReportId, newValue);
+ }
+ if (command.isChangeInStringParameterNamed(SmsCampaignValidator.recurrenceParamName, this.recurrence)) {
+ final String newValue = command.stringValueOfParameterNamed(SmsCampaignValidator.recurrenceParamName);
+ actualChanges.put(SmsCampaignValidator.recurrenceParamName, newValue);
+ this.recurrence = StringUtils.defaultIfEmpty(newValue, null);
+ }
+ if (command.isChangeInLongParameterNamed(SmsCampaignValidator.providerId, this.providerId)) {
+ final Long newValue = command.longValueOfParameterNamed(SmsCampaignValidator.providerId);
+ actualChanges.put(SmsCampaignValidator.providerId, newValue);
+ }
+
+ if (SmsCampaignTriggerType.fromInt(this.triggerType).isSchedule()) {
+ final String dateFormatAsInput = command.dateFormat();
+ final String dateTimeFormatAsInput = command.stringValueOfParameterNamed(SmsCampaignValidator.dateTimeFormat);
+ final String localeAsInput = command.locale();
+ final Locale locale = command.extractLocale();
+ final DateTimeFormatter fmt = DateTimeFormat.forPattern(dateTimeFormatAsInput).withLocale(locale);
+ final String valueAsInput = command.stringValueOfParameterNamed(SmsCampaignValidator.recurrenceStartDate);
+ actualChanges.put(SmsCampaignValidator.recurrenceStartDate, valueAsInput);
+ actualChanges.put(SmsCampaignValidator.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(SmsCampaignValidator.dateTimeFormat, dateTimeFormatAsInput);
+ actualChanges.put(SmsCampaignValidator.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,
+ SmsCampaignValidator.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 = SmsCampaignStatus.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,
+ SmsCampaignValidator.statusParamName, SmsCampaignStatus.fromInt(this.status).getCode());
+
+ final List dataValidationErrors = new ArrayList<>();
+ dataValidationErrors.add(error);
+
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+ if (this.triggerType.intValue() == SmsCampaignTriggerType.SCHEDULE.getValue()) {
+ this.nextTriggerDate = null;
+ this.lastTriggerDate = null;
+ }
+ this.closedBy = currentUser;
+ this.closureDate = closureLocalDate.toDate();
+ this.status = SmsCampaignStatus.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,
+ SmsCampaignValidator.statusParamName, SmsCampaignStatus.fromInt(this.status).getCode());
+
+ final List dataValidationErrors = new ArrayList<>();
+ dataValidationErrors.add(error);
+
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+
+ this.approvedOnDate = reactivateLocalDate.toDate();
+ this.status = SmsCampaignStatus.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,
+ SmsCampaignValidator.statusParamName, SmsCampaignStatus.fromInt(this.status).getCode());
+
+ final List dataValidationErrors = new ArrayList<>();
+ dataValidationErrors.add(error);
+
+ throw new PlatformApiDataValidationException(dataValidationErrors);
+ }
+ this.isVisible = false;
+ }
+
+ public boolean isActive() {
+ return SmsCampaignStatus.fromInt(this.status).isActive();
+ }
+
+ public boolean isPending() {
+ return SmsCampaignStatus.fromInt(this.status).isPending();
+ }
+
+ public boolean isClosed() {
+ return SmsCampaignStatus.fromInt(this.status).isClosed();
+ }
+
+ public boolean isDirect() {
+ return SmsCampaignTriggerType.fromInt(this.triggerType).isDirect();
+ }
+
+ public boolean isSchedule() {
+ return SmsCampaignTriggerType.fromInt(this.triggerType).isSchedule();
+ }
+
+ public boolean isTriggered() {
+ return SmsCampaignTriggerType.fromInt(this.triggerType).isTriggered();
+ }
+
+ 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, SmsCampaignValidator.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, SmsCampaignValidator.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, SmsCampaignValidator.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, SmsCampaignValidator.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, SmsCampaignValidator.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, SmsCampaignValidator.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, SmsCampaignValidator.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 getMessage() {
+ return this.message;
+ }
+
+ 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 Long getProviderId() {
+ return this.providerId;
+ }
+
+ private static String constructRecurrence(final JsonCommand command) {
+ final Integer frequency = command.integerValueOfParameterNamed(SmsCampaignValidator.frequencyParamName);
+ final CalendarFrequencyType frequencyType = CalendarFrequencyType.fromInt(frequency);
+ final Integer interval = command.integerValueOfParameterNamed(SmsCampaignValidator.intervalParamName);
+ Integer repeatsOnDay = null;
+ if (frequencyType.isWeekly()) {
+ repeatsOnDay = command.integerValueOfParameterNamed(SmsCampaignValidator.repeatsOnDayParamName);
+ }
+ return constructRecurrence(frequencyType, interval, repeatsOnDay);
+ }
+
+ private static String constructRecurrence(final CalendarFrequencyType frequencyType, final Integer interval, final Integer repeatsOnDay) {
+ final StringBuilder recurrenceBuilder = new StringBuilder(200);
+
+ recurrenceBuilder.append("FREQ=");
+ recurrenceBuilder.append(frequencyType.toString().toUpperCase());
+ if (interval > 1) {
+ recurrenceBuilder.append(";INTERVAL=");
+ recurrenceBuilder.append(interval);
+ }
+ if (frequencyType.isWeekly()) {
+ if (repeatsOnDay != null) {
+ final CalendarWeekDaysType weekDays = CalendarWeekDaysType.fromInt(repeatsOnDay);
+ if (!weekDays.isInvalid()) {
+ recurrenceBuilder.append(";BYDAY=");
+ recurrenceBuilder.append(weekDays.toString().toUpperCase());
+ }
+ }
+ }
+ return recurrenceBuilder.toString();
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java
new file mode 100644
index 000000000..436f9ed63
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java
@@ -0,0 +1,35 @@
+/**
+ * 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.sms.domain;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+public interface SmsCampaignRepository extends JpaRepository, JpaSpecificationExecutor {
+
+ List findByCampaignType(final Integer campaignType);
+
+ Collection findByCampaignTypeAndTriggerTypeAndStatus(final Integer campaignType, final Integer triggerType,
+ final Integer status);
+
+ Collection findByTriggerType(final Integer triggerType) ;
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignStatusEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignStatusEnumerations.java
new file mode 100644
index 000000000..9147a29f5
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignStatusEnumerations.java
@@ -0,0 +1,55 @@
+/**
+ * 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.sms.domain;
+
+import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignStatus;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+
+public class SmsCampaignStatusEnumerations {
+ public static EnumOptionData status(final Integer statusId) {
+ return status(SmsCampaignStatus.fromInt(statusId));
+ }
+
+ public static EnumOptionData status(final SmsCampaignStatus status) {
+ EnumOptionData optionData = new EnumOptionData(SmsCampaignStatus.INVALID.getValue().longValue(),
+ SmsCampaignStatus.INVALID.getCode(), "Invalid");
+ switch (status) {
+ case INVALID:
+ optionData = new EnumOptionData
+ (SmsCampaignStatus.INVALID.getValue().longValue(),
+ SmsCampaignStatus.INVALID.getCode(), "Invalid");
+ break;
+ case PENDING:
+ optionData = new EnumOptionData(SmsCampaignStatus.PENDING.getValue().longValue(),
+ SmsCampaignStatus.PENDING.getCode(), "Pending");
+ break;
+ case ACTIVE:
+ optionData = new EnumOptionData(SmsCampaignStatus.ACTIVE.getValue().longValue(), SmsCampaignStatus.ACTIVE.getCode(),
+ "active");
+ break;
+ case CLOSED:
+ optionData = new EnumOptionData(SmsCampaignStatus.CLOSED.getValue().longValue(),
+ SmsCampaignStatus.CLOSED.getCode(), "closed");
+ break;
+
+ }
+
+ return optionData;
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/ConnectionFailureException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/ConnectionFailureException.java
new file mode 100644
index 000000000..b7d9d976c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/ConnectionFailureException.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.sms.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformServiceUnavailableException;
+
+public class ConnectionFailureException extends AbstractPlatformServiceUnavailableException {
+
+ public ConnectionFailureException(final String connectionName) {
+ super("error.msg.unable.to.connect.to.intermediate.server", "Unable to connect to the server with connection name - "
+ + connectionName, connectionName);
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/GatewayConnectionConfigurationNotFound.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/GatewayConnectionConfigurationNotFound.java
new file mode 100644
index 000000000..cf9a31a24
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/GatewayConnectionConfigurationNotFound.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.sms.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+
+public class GatewayConnectionConfigurationNotFound extends AbstractPlatformResourceNotFoundException {
+
+ public GatewayConnectionConfigurationNotFound(String connectionName) {
+ super("error.msg.gateway.config.not.found", "Gateway Connection Configuration with name `" + connectionName + "` does not exist",
+ connectionName);
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignMustBeClosedToBeDeletedException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignMustBeClosedToBeDeletedException.java
new file mode 100644
index 000000000..96fd8d3d7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignMustBeClosedToBeDeletedException.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.sms.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
+
+public class SmsCampaignMustBeClosedToBeDeletedException extends AbstractPlatformDomainRuleException {
+
+ public SmsCampaignMustBeClosedToBeDeletedException(final Long resourceId) {
+ super("error.msg.sms.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/sms/exception/SmsCampaignMustBeClosedToEditException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignMustBeClosedToEditException.java
new file mode 100644
index 000000000..5798d8f7e
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignMustBeClosedToEditException.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.sms.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
+
+public class SmsCampaignMustBeClosedToEditException extends AbstractPlatformDomainRuleException {
+
+ public SmsCampaignMustBeClosedToEditException(final Long resourceId) {
+ super("error.msg.sms.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/sms/exception/SmsCampaignNotFound.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignNotFound.java
new file mode 100644
index 000000000..6bf0f4746
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/exception/SmsCampaignNotFound.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.sms.exception;
+
+import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
+
+public class SmsCampaignNotFound extends AbstractPlatformResourceNotFoundException{
+
+ public SmsCampaignNotFound(final Long resourceId) {
+ super("error.msg.sms.campaign.identifier.not.found", "Sms Campaign with identifier `" + resourceId + "` does not exist", resourceId);
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/ActivateSmsCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/ActivateSmsCampaignCommandHandler.java
new file mode 100644
index 000000000..a6b56d353
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/ActivateSmsCampaignCommandHandler.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.sms.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "SMSCAMPAIGN", action = "ACTIVATE")
+public class ActivateSmsCampaignCommandHandler implements NewCommandSourceHandler {
+
+ private SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ @Autowired
+ public ActivateSmsCampaignCommandHandler(final SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.smsCampaignWritePlatformService.activateSmsCampaign(command.entityId(), command);
+ }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/CloseSmsCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/CloseSmsCampaignCommandHandler.java
new file mode 100644
index 000000000..0f1217e11
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/CloseSmsCampaignCommandHandler.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.sms.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@CommandType(entity = "SMSCAMPAIGN", action = "CLOSE")
+public class CloseSmsCampaignCommandHandler implements NewCommandSourceHandler {
+ private final SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ @Autowired
+ public CloseSmsCampaignCommandHandler(final SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.smsCampaignWritePlatformService.closeSmsCampaign(command.entityId(), command);
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/CreateSmsCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/CreateSmsCampaignCommandHandler.java
new file mode 100644
index 000000000..687d9af16
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/CreateSmsCampaignCommandHandler.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.sms.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "SMSCAMPAIGN", action = "CREATE")
+public class CreateSmsCampaignCommandHandler implements NewCommandSourceHandler {
+
+ private SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ @Autowired
+ public CreateSmsCampaignCommandHandler(final SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.smsCampaignWritePlatformService.create(command);
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/DeleteSmsCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/DeleteSmsCampaignCommandHandler.java
new file mode 100644
index 000000000..ebb5ca505
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/DeleteSmsCampaignCommandHandler.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.sms.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "SMSCAMPAIGN", action = "DELETE")
+public class DeleteSmsCampaignCommandHandler implements NewCommandSourceHandler {
+
+ private final SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ @Autowired
+ public DeleteSmsCampaignCommandHandler(SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.smsCampaignWritePlatformService.delete(command.entityId());
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/ReactivateSmsCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/ReactivateSmsCampaignCommandHandler.java
new file mode 100644
index 000000000..aa8bebe6f
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/ReactivateSmsCampaignCommandHandler.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.sms.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "SMSCAMPAIGN", action = "REACTIVATE")
+public class ReactivateSmsCampaignCommandHandler implements NewCommandSourceHandler {
+
+ private final SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ @Autowired
+ public ReactivateSmsCampaignCommandHandler(SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.smsCampaignWritePlatformService.reactivateSmsCampaign(command.entityId(),command);
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/UpdateSmsCampaignCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/UpdateSmsCampaignCommandHandler.java
new file mode 100644
index 000000000..e3c7f6615
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/handler/UpdateSmsCampaignCommandHandler.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.sms.handler;
+
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.campaigns.sms.service.SmsCampaignWritePlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@CommandType(entity = "SMSCAMPAIGN", action = "UPDATE")
+public class UpdateSmsCampaignCommandHandler implements NewCommandSourceHandler {
+
+ private SmsCampaignWritePlatformService smsCampaignWritePlatformService;
+
+ @Autowired
+ public UpdateSmsCampaignCommandHandler(final SmsCampaignWritePlatformService smsCampaignWritePlatformService) {
+ this.smsCampaignWritePlatformService = smsCampaignWritePlatformService;
+ }
+
+ @Transactional
+ @Override
+ public CommandProcessingResult processCommand(JsonCommand command) {
+ return this.smsCampaignWritePlatformService.update(command.entityId(), command);
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java
new file mode 100644
index 000000000..948405490
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java
@@ -0,0 +1,302 @@
+/**
+ * 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.sms.serialization;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
+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.portfolio.calendar.domain.CalendarFrequencyType;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.JsonElement;
+import com.google.gson.reflect.TypeToken;
+
+@Component
+public class SmsCampaignValidator {
+
+ public static final String RESOURCE_NAME = "sms";
+ public static final String campaignName = "campaignName";
+ public static final String campaignType = "campaignType";
+ public static final String triggerType = "triggerType";
+ public static final String triggerEntityType = "triggerEntityType";
+ public static final String triggerActionType = "triggerActionType";
+ public static final String providerId = "providerId";
+ public static final String runReportId = "runReportId";
+ public static final String paramValue = "paramValue";
+ public static final String message = "message";
+ public static final String activationDateParamName = "activationDate";
+ public static final String recurrenceStartDate = "recurrenceStartDate";
+ public static final String dateTimeFormat = "dateTimeFormat";
+ 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";
+ public static final String frequencyParamName = "frequency";
+ public static final String intervalParamName = "interval";
+ public static final String repeatsOnDayParamName = "repeatsOnDay";
+
+ private final FromJsonHelper fromApiJsonHelper;
+
+ public static final Set supportedParams = new HashSet<>(Arrays.asList(campaignName, campaignType, localeParamName,
+ dateFormatParamName, runReportId, paramValue, message, recurrenceStartDate, activationDateParamName, submittedOnDateParamName,
+ closureDateParamName, recurrenceParamName, providerId, triggerType, frequencyParamName, intervalParamName,
+ repeatsOnDayParamName, triggerEntityType, triggerActionType, dateTimeFormat));
+
+ public static final Set supportedParamsForUpdate = new HashSet<>(Arrays.asList(campaignName, campaignType, localeParamName,
+ dateFormatParamName, runReportId, paramValue, message, recurrenceStartDate, activationDateParamName, recurrenceParamName,
+ providerId, triggerType, triggerEntityType, triggerActionType, dateTimeFormat));
+
+ 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, message));
+
+ @Autowired
+ public SmsCampaignValidator(FromJsonHelper fromApiJsonHelper) {
+ this.fromApiJsonHelper = fromApiJsonHelper;
+ }
+
+ public void validateCreate(String json) {
+ if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
+
+ final Type typeOfMap = new TypeToken