MIFOSX-119: basic CRUD outline for sms outbound messages for platform. no specific attempt at using third party lib or service to actually send an SMS included here.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
DROP TABLE IF EXISTS `sms_messages_outbound`;
|
||||
CREATE TABLE `sms_messages_outbound` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` bigint(20) DEFAULT NULL,
|
||||
`client_id` bigint(20) DEFAULT NULL,
|
||||
`staff_id` bigint(20) DEFAULT NULL,
|
||||
`status_enum` int(5) NOT NULL DEFAULT '100',
|
||||
`mobile_no` varchar(50) NOT NULL,
|
||||
`message` varchar(1000) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FKGROUP000000001` (`group_id`),
|
||||
KEY `FKCLIENT00000001` (`client_id`),
|
||||
CONSTRAINT `FKGROUP000000001` FOREIGN KEY (`group_id`) REFERENCES `m_group` (`id`),
|
||||
CONSTRAINT `FKCLIENT00000001` FOREIGN KEY (`client_id`) REFERENCES `m_client` (`id`),
|
||||
CONSTRAINT `FKSTAFF000000001` FOREIGN KEY (`staff_id`) REFERENCES `m_staff` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- Permissions for CRUD on SMS outbound message tracking
|
||||
DELETE FROM `m_permission` WHERE `entity_name`='SMS';
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'READ_SMS', 'SMS', 'READ', 0);
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'CREATE_SMS', 'SMS', 'CREATE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'CREATE_SMS_CHECKER', 'SMS', 'CREATE', 0);
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'UPDATE_SMS', 'SMS', 'UPDATE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'UPDATE_SMS_CHECKER', 'SMS', 'UPDATE', 0);
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'DELETE_SMS', 'SMS', 'DELETE', 0);
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('organisation', 'DELETE_SMS_CHECKER', 'SMS', 'DELETE', 0);
|
||||
Vendored
+4
-1
@@ -7,7 +7,10 @@
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
DEFAULT_JVM_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,suspend=n"
|
||||
|
||||
CATALINA_OPTS="-Xms512m -Xmx512m"
|
||||
JAVA_OPTS="-Xms512m -Xmx512m -XX:PermSize=256m -XX:MaxPermSize=512m"
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
Vendored
+4
-1
@@ -9,7 +9,10 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
set DEFAULT_JVM_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8005,server=y,suspend=n
|
||||
|
||||
set CATALINA_OPTS=-Xms512m -Xmx512m
|
||||
set JAVA_OPTS=-Xms512m -Xmx512m -XX:PermSize=256m -XX:MaxPermSize=512m
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
|
||||
+7
-3
@@ -143,7 +143,7 @@ public class CommandWrapper {
|
||||
public String getTransactionId() {
|
||||
return this.transactionId;
|
||||
}
|
||||
|
||||
|
||||
public Long getTemplateId() {
|
||||
return this.templateId;
|
||||
}
|
||||
@@ -230,6 +230,10 @@ public class CommandWrapper {
|
||||
return this.entityName.equalsIgnoreCase("CURRENCY");
|
||||
}
|
||||
|
||||
public boolean isSmsResource() {
|
||||
return this.entityName.equalsIgnoreCase("SMS");
|
||||
}
|
||||
|
||||
public boolean isCodeResource() {
|
||||
return this.entityName.equalsIgnoreCase("CODE");
|
||||
}
|
||||
@@ -265,7 +269,7 @@ public class CommandWrapper {
|
||||
public boolean isUpdateRunningbalance() {
|
||||
return this.actionName.equalsIgnoreCase("UPDATERUNNINGBALANCE") && this.entityName.equalsIgnoreCase("JOURNALENTRY");
|
||||
}
|
||||
|
||||
|
||||
public boolean isFundResource() {
|
||||
return this.entityName.equalsIgnoreCase("FUND");
|
||||
}
|
||||
@@ -329,7 +333,7 @@ public class CommandWrapper {
|
||||
public boolean isCollateralResource() {
|
||||
return this.entityName.equalsIgnoreCase("COLLATERAL");
|
||||
}
|
||||
|
||||
|
||||
public boolean isTemplateRessource() {
|
||||
return this.entityName.equalsIgnoreCase("Template");
|
||||
}
|
||||
|
||||
+26
-2
@@ -238,6 +238,30 @@ public class CommandWrapperBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createSms() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "SMS";
|
||||
this.entityId = null;
|
||||
this.href = "/sms/template";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateSms(final Long resourceId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "SMS";
|
||||
this.entityId = resourceId;
|
||||
this.href = "/sms/" + resourceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder deleteSms(final Long resourceId) {
|
||||
this.actionName = "DELETE";
|
||||
this.entityName = "SMS";
|
||||
this.entityId = resourceId;
|
||||
this.href = "/sms/" + resourceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createCode() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "CODE";
|
||||
@@ -362,7 +386,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/clientId/" + clientId + "?command=proposeTransfer";
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder proposeAndAcceptClientTransfer(final Long clientId) {
|
||||
this.actionName = "PROPOSEANDACCEPTTRANSFER";
|
||||
this.entityName = "CLIENT";
|
||||
@@ -773,7 +797,7 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/journalentries/" + transactionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public CommandWrapperBuilder updateRunningBalanceForJournalEntry() {
|
||||
this.actionName = "UPDATERUNNINGBALANCE";
|
||||
this.entityName = "JOURNALENTRY";
|
||||
|
||||
+10
@@ -254,6 +254,16 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else if (wrapper.isSmsResource()) {
|
||||
if (wrapper.isCreate()) {
|
||||
handler = this.applicationContext.getBean("createSmsCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isUpdate()) {
|
||||
handler = this.applicationContext.getBean("updateSmsCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isDelete()) {
|
||||
handler = this.applicationContext.getBean("deleteSmsCommandHandler", NewCommandSourceHandler.class);
|
||||
} else {
|
||||
throw new UnsupportedCommandException(wrapper.commandName());
|
||||
}
|
||||
} else if (wrapper.isCurrencyResource()) {
|
||||
handler = this.applicationContext.getBean("updateCurrencyCommandHandler", NewCommandSourceHandler.class);
|
||||
} else if (wrapper.isFundResource()) {
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.core.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.ExclusionStrategy;
|
||||
import com.google.gson.FieldAttributes;
|
||||
|
||||
public class ParameterListInclusionStrategy implements ExclusionStrategy {
|
||||
|
||||
private final Set<String> parameterNamesToInclude;
|
||||
|
||||
public ParameterListInclusionStrategy(final Set<String> parameterNamesToSkip) {
|
||||
this.parameterNamesToInclude = parameterNamesToSkip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSkipField(final FieldAttributes f) {
|
||||
return !this.parameterNamesToInclude.contains(f.getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Override
|
||||
public boolean shouldSkipClass(final Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+27
@@ -80,6 +80,24 @@ public final class DefaultToApiJsonSerializer<T> implements ToApiJsonSerializer<
|
||||
return serializeWithSettings(delegatedSerializer, settings, singleObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final ApiRequestJsonSerializationSettings settings, final Collection<T> collection) {
|
||||
final Gson delegatedSerializer = findAppropriateSerializer(settings);
|
||||
return serializeWithSettings(delegatedSerializer, settings, collection.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final ApiRequestJsonSerializationSettings settings, final T singleObject) {
|
||||
final Gson delegatedSerializer = findAppropriateSerializer(settings);
|
||||
return serializeWithSettings(delegatedSerializer, settings, singleObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serialize(final ApiRequestJsonSerializationSettings settings, final Page<T> singleObject) {
|
||||
final Gson delegatedSerializer = findAppropriateSerializer(settings);
|
||||
return serializeWithSettings(delegatedSerializer, settings, singleObject);
|
||||
}
|
||||
|
||||
private String serializeWithSettings(final Gson gson, final ApiRequestJsonSerializationSettings settings, final Object[] dataObject) {
|
||||
String json = null;
|
||||
if (gson != null) {
|
||||
@@ -116,4 +134,13 @@ public final class DefaultToApiJsonSerializer<T> implements ToApiJsonSerializer<
|
||||
}
|
||||
return gson;
|
||||
}
|
||||
|
||||
private Gson findAppropriateSerializer(final ApiRequestJsonSerializationSettings settings) {
|
||||
Gson gson = null;
|
||||
if (settings.isPartialResponseRequired()) {
|
||||
gson = this.helper.createGsonBuilderForPartialResponseFiltering(settings.isPrettyPrint(),
|
||||
settings.getParametersForPartialResponse());
|
||||
}
|
||||
return gson;
|
||||
}
|
||||
}
|
||||
+15
@@ -16,6 +16,7 @@ import org.mifosplatform.infrastructure.core.api.JodaDateTimeAdapter;
|
||||
import org.mifosplatform.infrastructure.core.api.JodaLocalDateAdapter;
|
||||
import org.mifosplatform.infrastructure.core.api.JodaMonthDayAdapter;
|
||||
import org.mifosplatform.infrastructure.core.api.ParameterListExclusionStrategy;
|
||||
import org.mifosplatform.infrastructure.core.api.ParameterListInclusionStrategy;
|
||||
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -40,6 +41,20 @@ public final class GoogleGsonSerializerHelper {
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
public Gson createGsonBuilderForPartialResponseFiltering(final boolean prettyPrint, final Set<String> responseParameters) {
|
||||
|
||||
final ExclusionStrategy strategy = new ParameterListInclusionStrategy(responseParameters);
|
||||
|
||||
final GsonBuilder builder = new GsonBuilder().addSerializationExclusionStrategy(strategy);
|
||||
builder.registerTypeAdapter(LocalDate.class, new JodaLocalDateAdapter());
|
||||
builder.registerTypeAdapter(DateTime.class, new JodaDateTimeAdapter());
|
||||
builder.registerTypeAdapter(MonthDay.class, new JodaMonthDayAdapter());
|
||||
if (prettyPrint) {
|
||||
builder.setPrettyPrinting();
|
||||
}
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
public Gson createGsonBuilderWithParameterExclusionSerializationStrategy(final Set<String> supportedParameters,
|
||||
final boolean prettyPrint, final Set<String> responseParameters) {
|
||||
|
||||
|
||||
+11
@@ -18,6 +18,17 @@ public interface ToApiJsonSerializer<T> {
|
||||
|
||||
String serializeResult(Object object);
|
||||
|
||||
String serialize(ApiRequestJsonSerializationSettings settings, Collection<T> collection);
|
||||
|
||||
String serialize(ApiRequestJsonSerializationSettings settings, T single);
|
||||
|
||||
String serialize(ApiRequestJsonSerializationSettings settings, Page<T> singleObject);
|
||||
|
||||
// TODO: TECHDEBT - bottom three will be deprecated going forward to remove
|
||||
// need for people to pass full list of supported parameters. It was only
|
||||
// used in cases where the partial response features was used (fields=x,y,x)
|
||||
// to report error if incorrect field was passed. From now on it will just
|
||||
// ignore unknown fields and fail silently
|
||||
String serialize(ApiRequestJsonSerializationSettings settings, Collection<T> collection, Set<String> supportedResponseParameters);
|
||||
|
||||
String serialize(ApiRequestJsonSerializationSettings settings, T single, Set<String> supportedResponseParameters);
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class SmsApiConstants {
|
||||
|
||||
public static final String RESOURCE_NAME = "sms";
|
||||
|
||||
// general
|
||||
public static final String localeParamName = "locale";
|
||||
public static final String dateFormatParamName = "dateFormat";
|
||||
|
||||
// request parameters
|
||||
public static final String idParamName = "id";
|
||||
public static final String groupIdParamName = "groupId";
|
||||
public static final String clientIdParamName = "clientId";
|
||||
public static final String staffIdParamName = "staffId";
|
||||
public static final String messageParamName = "message";
|
||||
|
||||
// response parameters
|
||||
public static final String statusParamName = "status";
|
||||
|
||||
public static final Set<String> CREATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(localeParamName,
|
||||
dateFormatParamName, groupIdParamName, clientIdParamName, staffIdParamName, messageParamName));
|
||||
|
||||
public static final Set<String> UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<String>(Arrays.asList(messageParamName));
|
||||
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.api;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
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.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.infrastructure.sms.data.SmsData;
|
||||
import org.mifosplatform.infrastructure.sms.service.SmsReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/sms")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class SmsApiResource {
|
||||
|
||||
private final String resourceNameForPermissions = "SMS";
|
||||
|
||||
private final PlatformSecurityContext context;
|
||||
private final SmsReadPlatformService readPlatformService;
|
||||
private final DefaultToApiJsonSerializer<SmsData> toApiJsonSerializer;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
|
||||
@Autowired
|
||||
public SmsApiResource(final PlatformSecurityContext context, final SmsReadPlatformService readPlatformService,
|
||||
final DefaultToApiJsonSerializer<SmsData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
|
||||
this.context = context;
|
||||
this.readPlatformService = readPlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
}
|
||||
|
||||
@GET
|
||||
public String retrieveAll(@Context final UriInfo uriInfo) {
|
||||
|
||||
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermissions);
|
||||
|
||||
final Collection<SmsData> smsMessages = this.readPlatformService.retrieveAll();
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, smsMessages);
|
||||
}
|
||||
|
||||
@POST
|
||||
public String create(final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().createSms().withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{resourceId}")
|
||||
public String retrieveOne(@PathParam("resourceId") final Long resourceId, @Context final UriInfo uriInfo) {
|
||||
|
||||
final SmsData smsMessage = this.readPlatformService.retrieveOne(resourceId);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, smsMessage);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{resourceId}")
|
||||
public String update(@PathParam("resourceId") final Long resourceId, final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateSms(resourceId).withJson(apiRequestBodyAsJson).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{resourceId}")
|
||||
public String delete(@PathParam("resourceId") final Long resourceId) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteSms(resourceId).build();
|
||||
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.data;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
|
||||
/**
|
||||
* Immutable data object representing a SMS message.
|
||||
*/
|
||||
public class SmsData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final Long id;
|
||||
@SuppressWarnings("unused")
|
||||
private final Long groupId;
|
||||
@SuppressWarnings("unused")
|
||||
private final Long clientId;
|
||||
@SuppressWarnings("unused")
|
||||
private final Long staffId;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData status;
|
||||
@SuppressWarnings("unused")
|
||||
private final String mobileNo;
|
||||
@SuppressWarnings("unused")
|
||||
private final String message;
|
||||
|
||||
public static SmsData instance(final Long id, final Long groupId, final Long clientId, final Long staffId, final EnumOptionData status,
|
||||
final String mobileNo, final String message) {
|
||||
return new SmsData(id, groupId, clientId, staffId, status, mobileNo, message);
|
||||
}
|
||||
|
||||
private SmsData(final Long id, final Long groupId, final Long clientId, final Long staffId, final EnumOptionData status,
|
||||
final String mobileNo, final String message) {
|
||||
this.id = id;
|
||||
this.groupId = groupId;
|
||||
this.clientId = clientId;
|
||||
this.staffId = staffId;
|
||||
this.status = status;
|
||||
this.mobileNo = mobileNo;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.data;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.sms.SmsApiConstants;
|
||||
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 final class SmsDataValidator {
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public SmsDataValidator(final FromJsonHelper fromApiJsonHelper) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public void validateForCreate(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SmsApiConstants.CREATE_REQUEST_DATA_PARAMETERS);
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SmsApiConstants.RESOURCE_NAME);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.groupIdParamName, element)) {
|
||||
final Long groupId = this.fromApiJsonHelper.extractLongNamed(SmsApiConstants.groupIdParamName, element);
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.groupIdParamName).value(groupId).notNull().integerGreaterThanZero();
|
||||
|
||||
// ensure clientId and staffId are not passed
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.clientIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.clientIdParamName).failWithCode("cannot.be.passed.with.groupId");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.staffIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.staffIdParamName).failWithCode("cannot.be.passed.with.groupId");
|
||||
}
|
||||
} else if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.clientIdParamName, element)) {
|
||||
final Long clientId = this.fromApiJsonHelper.extractLongNamed(SmsApiConstants.clientIdParamName, element);
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.clientIdParamName).value(clientId).notNull().integerGreaterThanZero();
|
||||
|
||||
// ensure groupId and staffId are not passed
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.groupIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.groupIdParamName).failWithCode("cannot.be.passed.with.clientId");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.staffIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.staffIdParamName).failWithCode("cannot.be.passed.with.clientId");
|
||||
}
|
||||
} else if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.staffIdParamName, element)) {
|
||||
final Long staffId = this.fromApiJsonHelper.extractLongNamed(SmsApiConstants.staffIdParamName, element);
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.staffIdParamName).value(staffId).ignoreIfNull().longGreaterThanZero();
|
||||
|
||||
// ensure groupId and clientId are not passed
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.groupIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.groupIdParamName).failWithCode("cannot.be.passed.with.staffId");
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.clientIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.clientIdParamName).failWithCode("cannot.be.passed.with.staffId");
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.fromApiJsonHelper.parameterExists(SmsApiConstants.groupIdParamName, element)
|
||||
&& !this.fromApiJsonHelper.parameterExists(SmsApiConstants.clientIdParamName, element)
|
||||
&& !this.fromApiJsonHelper.parameterExists(SmsApiConstants.staffIdParamName, element)) {
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.staffIdParamName)
|
||||
.failWithCodeNoParameterAddedToErrorCode("no.entity.provided");
|
||||
}
|
||||
|
||||
final String message = this.fromApiJsonHelper.extractStringNamed(SmsApiConstants.messageParamName, element);
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.messageParamName).value(message).notBlank().notExceedingLengthOf(1000);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
public void validateForUpdate(final String json) {
|
||||
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, SmsApiConstants.UPDATE_REQUEST_DATA_PARAMETERS);
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
|
||||
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
.resource(SmsApiConstants.RESOURCE_NAME);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.messageParamName, element)) {
|
||||
final String message = this.fromApiJsonHelper.extractStringNamed(SmsApiConstants.messageParamName, element);
|
||||
baseDataValidator.reset().parameter(SmsApiConstants.messageParamName).value(message).notBlank().notExceedingLengthOf(1000);
|
||||
}
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.domain;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
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 org.apache.commons.lang3.StringUtils;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.sms.SmsApiConstants;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sms_messages_outbound")
|
||||
public class SmsMessage extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "group_id", nullable = true)
|
||||
private Group group;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "client_id", nullable = true)
|
||||
private Client client;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "staff_id", nullable = true)
|
||||
private Staff staff;
|
||||
|
||||
@Column(name = "status_enum", nullable = false)
|
||||
private Integer statusType;
|
||||
|
||||
@Column(name = "mobile_no", nullable = false)
|
||||
private String mobileNo;
|
||||
|
||||
@Column(name = "message", nullable = false)
|
||||
private String message;
|
||||
|
||||
public static SmsMessage pendingSms(final Group group, final Client client, final Staff staff, final String message,
|
||||
final String mobileNo) {
|
||||
return new SmsMessage(group, client, staff, SmsMessageStatusType.PENDING, message, mobileNo);
|
||||
}
|
||||
|
||||
protected SmsMessage() {
|
||||
//
|
||||
}
|
||||
|
||||
private SmsMessage(final Group group, final Client client, final Staff staff, final SmsMessageStatusType statusType,
|
||||
final String message, final String mobileNo) {
|
||||
this.group = group;
|
||||
this.client = client;
|
||||
this.staff = staff;
|
||||
this.statusType = statusType.getValue();
|
||||
this.mobileNo = mobileNo;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(1);
|
||||
|
||||
if (command.isChangeInStringParameterNamed(SmsApiConstants.messageParamName, this.message)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(SmsApiConstants.messageParamName);
|
||||
actualChanges.put(SmsApiConstants.messageParamName, newValue);
|
||||
this.message = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.domain;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.sms.SmsApiConstants;
|
||||
import org.mifosplatform.infrastructure.sms.exception.SmsNotFoundException;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
@Component
|
||||
public class SmsMessageAssembler {
|
||||
|
||||
private final SmsMessageRepository smsMessageRepository;
|
||||
private final GroupRepositoryWrapper groupRepository;
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final StaffRepositoryWrapper staffRepository;
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@Autowired
|
||||
public SmsMessageAssembler(final SmsMessageRepository smsMessageRepository,
|
||||
final GroupRepositoryWrapper groupRepositoryWrapper,
|
||||
final ClientRepositoryWrapper clientRepository,
|
||||
final StaffRepositoryWrapper staffRepository,
|
||||
final FromJsonHelper fromApiJsonHelper) {
|
||||
this.smsMessageRepository = smsMessageRepository;
|
||||
this.groupRepository = groupRepositoryWrapper;
|
||||
this.clientRepository = clientRepository;
|
||||
this.staffRepository = staffRepository;
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
}
|
||||
|
||||
public SmsMessage assembleFromJson(final JsonCommand command) {
|
||||
|
||||
final JsonElement element = command.parsedJson();
|
||||
|
||||
String mobileNo = null;
|
||||
|
||||
Group group = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.groupIdParamName, element)) {
|
||||
final Long groupId = this.fromApiJsonHelper.extractLongNamed(SmsApiConstants.groupIdParamName, element);
|
||||
group = this.groupRepository.findOneWithNotFoundDetection(groupId);
|
||||
mobileNo = "+353 87 3455667";
|
||||
}
|
||||
|
||||
Client client = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.clientIdParamName, element)) {
|
||||
final Long clientId = this.fromApiJsonHelper.extractLongNamed(SmsApiConstants.clientIdParamName, element);
|
||||
client = this.clientRepository.findOneWithNotFoundDetection(clientId);
|
||||
mobileNo = "+353 87 3455667"; // get from entity
|
||||
}
|
||||
|
||||
Staff staff = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(SmsApiConstants.staffIdParamName, element)) {
|
||||
final Long staffId = this.fromApiJsonHelper.extractLongNamed(SmsApiConstants.staffIdParamName, element);
|
||||
staff = this.staffRepository.findOneWithNotFoundDetection(staffId);
|
||||
mobileNo = "+353 87 3455667"; // get from entity
|
||||
}
|
||||
|
||||
final String message = this.fromApiJsonHelper.extractStringNamed(SmsApiConstants.messageParamName, element);
|
||||
|
||||
return SmsMessage.pendingSms(group, client, staff, message, mobileNo);
|
||||
}
|
||||
|
||||
public SmsMessage assembleFromResourceId(final Long resourceId) {
|
||||
final SmsMessage sms = this.smsMessageRepository.findOne(resourceId);
|
||||
if (sms == null) { throw new SmsNotFoundException(resourceId); }
|
||||
return sms;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.domain;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
|
||||
public class SmsMessageEnumerations {
|
||||
|
||||
public static EnumOptionData status(final Integer statusId) {
|
||||
return status(SmsMessageStatusType.fromInt(statusId));
|
||||
}
|
||||
|
||||
public static EnumOptionData status(final SmsMessageStatusType status) {
|
||||
EnumOptionData optionData = new EnumOptionData(SmsMessageStatusType.INVALID.getValue().longValue(), SmsMessageStatusType.INVALID.getCode(),
|
||||
"Invalid");
|
||||
switch (status) {
|
||||
case INVALID:
|
||||
optionData = new EnumOptionData(SmsMessageStatusType.INVALID.getValue().longValue(), SmsMessageStatusType.INVALID.getCode(), "Invalid");
|
||||
break;
|
||||
case PENDING:
|
||||
optionData = new EnumOptionData(SmsMessageStatusType.PENDING.getValue().longValue(), SmsMessageStatusType.PENDING.getCode(), "Pending");
|
||||
break;
|
||||
case SENT:
|
||||
optionData = new EnumOptionData(SmsMessageStatusType.SENT.getValue().longValue(), SmsMessageStatusType.SENT.getCode(), "Sent");
|
||||
break;
|
||||
case DELIVERED:
|
||||
optionData = new EnumOptionData(SmsMessageStatusType.DELIVERED.getValue().longValue(), SmsMessageStatusType.DELIVERED.getCode(), "Delivered");
|
||||
break;
|
||||
case FAILED:
|
||||
optionData = new EnumOptionData(SmsMessageStatusType.FAILED.getValue().longValue(), SmsMessageStatusType.FAILED.getCode(), "Failed");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return optionData;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
|
||||
public interface SmsMessageRepository extends JpaRepository<SmsMessage, Long>, JpaSpecificationExecutor<SmsMessage> {
|
||||
// no extra behaviour
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package org.mifosplatform.infrastructure.sms.domain;
|
||||
|
||||
public enum SmsMessageStatusType {
|
||||
|
||||
INVALID(0, "smsMessageStatusType.invalid"), //
|
||||
PENDING(100, "smsMessageStatusType.pending"), //
|
||||
SENT(200, "smsMessageStatusType.sent"), //
|
||||
DELIVERED(300, "smsMessageStatusType.delivered"), //
|
||||
FAILED(400, "smsMessageStatusType.failed");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
|
||||
public static SmsMessageStatusType fromInt(final Integer statusValue) {
|
||||
|
||||
SmsMessageStatusType enumeration = SmsMessageStatusType.INVALID;
|
||||
switch (statusValue) {
|
||||
case 100:
|
||||
enumeration = SmsMessageStatusType.PENDING;
|
||||
break;
|
||||
case 200:
|
||||
enumeration = SmsMessageStatusType.SENT;
|
||||
break;
|
||||
case 300:
|
||||
enumeration = SmsMessageStatusType.DELIVERED;
|
||||
break;
|
||||
case 400:
|
||||
enumeration = SmsMessageStatusType.FAILED;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
|
||||
private SmsMessageStatusType(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} thrown when a code is not found.
|
||||
*/
|
||||
public class SmsNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public SmsNotFoundException(final Long resourceId) {
|
||||
super("error.msg.sms.identifier.not.found", "SMS with identifier `" + resourceId + "` does not exist", resourceId);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.sms.service.SmsWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class CreateSmsCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SmsWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateSmsCommandHandler(final SmsWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.create(command);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.sms.service.SmsWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class DeleteSmsCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SmsWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public DeleteSmsCommandHandler(final SmsWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.delete(command.entityId());
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.handler;
|
||||
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.sms.service.SmsWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class UpdateSmsCommandHandler implements NewCommandSourceHandler {
|
||||
|
||||
private final SmsWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateSmsCommandHandler(final SmsWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
|
||||
return this.writePlatformService.update(command.entityId(), command);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.service;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.infrastructure.sms.data.SmsData;
|
||||
|
||||
public interface SmsReadPlatformService {
|
||||
|
||||
Collection<SmsData> retrieveAll();
|
||||
|
||||
SmsData retrieveOne(Long resourceId);
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.service;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.infrastructure.sms.data.SmsData;
|
||||
import org.mifosplatform.infrastructure.sms.domain.SmsMessageEnumerations;
|
||||
import org.mifosplatform.infrastructure.sms.exception.SmsNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class SmsReadPlatformServiceImpl implements SmsReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final SmsMapper smsRowMapper;
|
||||
|
||||
@Autowired
|
||||
public SmsReadPlatformServiceImpl(final RoutingDataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
this.smsRowMapper = new SmsMapper();
|
||||
}
|
||||
|
||||
private static final class SmsMapper implements RowMapper<SmsData> {
|
||||
|
||||
final String schema;
|
||||
|
||||
public SmsMapper() {
|
||||
final StringBuilder sql = new StringBuilder(300);
|
||||
sql.append("smo.id as id, ");
|
||||
sql.append("smo.group_id as groupId, ");
|
||||
sql.append("smo.client_id as clientId, ");
|
||||
sql.append("smo.staff_id as staffId, ");
|
||||
sql.append("smo.status_enum as statusId, ");
|
||||
sql.append("smo.mobile_no as mobileNo, ");
|
||||
sql.append("smo.message as message ");
|
||||
sql.append("from sms_messages_outbound smo");
|
||||
|
||||
this.schema = sql.toString();
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
|
||||
|
||||
final Long id = JdbcSupport.getLong(rs, "id");
|
||||
final Long groupId = JdbcSupport.getLong(rs, "groupId");
|
||||
final Long clientId = JdbcSupport.getLong(rs, "clientId");
|
||||
final Long staffId = JdbcSupport.getLong(rs, "staffId");
|
||||
|
||||
final String mobileNo = rs.getString("mobileNo");
|
||||
final String message = rs.getString("message");
|
||||
|
||||
final Integer statusId = JdbcSupport.getInteger(rs, "statusId");
|
||||
final EnumOptionData status = SmsMessageEnumerations.status(statusId);
|
||||
|
||||
return SmsData.instance(id, groupId, clientId, staffId, status, mobileNo, message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<SmsData> retrieveAll() {
|
||||
|
||||
final String sql = "select " + this.smsRowMapper.schema();
|
||||
|
||||
return this.jdbcTemplate.query(sql, this.smsRowMapper, new Object[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsData retrieveOne(final Long resourceId) {
|
||||
try {
|
||||
final String sql = "select " + this.smsRowMapper.schema() + " where smo.id = ?";
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sql, this.smsRowMapper, new Object[] { resourceId });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new SmsNotFoundException(resourceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface SmsWritePlatformService {
|
||||
|
||||
CommandProcessingResult create(JsonCommand command);
|
||||
|
||||
CommandProcessingResult update(Long resourceId, JsonCommand command);
|
||||
|
||||
CommandProcessingResult delete(Long resourceId);
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.infrastructure.sms.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.mifosplatform.infrastructure.sms.data.SmsDataValidator;
|
||||
import org.mifosplatform.infrastructure.sms.domain.SmsMessage;
|
||||
import org.mifosplatform.infrastructure.sms.domain.SmsMessageAssembler;
|
||||
import org.mifosplatform.infrastructure.sms.domain.SmsMessageRepository;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class SmsWritePlatformServiceJpaRepositoryImpl implements SmsWritePlatformService {
|
||||
|
||||
private final static Logger logger = LoggerFactory.getLogger(SmsWritePlatformServiceJpaRepositoryImpl.class);
|
||||
|
||||
private final SmsMessageAssembler assembler;
|
||||
private final SmsMessageRepository repository;
|
||||
private final SmsDataValidator validator;
|
||||
|
||||
@Autowired
|
||||
public SmsWritePlatformServiceJpaRepositoryImpl(final SmsMessageAssembler assembler, final SmsMessageRepository repository,
|
||||
final SmsDataValidator validator) {
|
||||
this.assembler = assembler;
|
||||
this.repository = repository;
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult create(final JsonCommand command) {
|
||||
|
||||
try {
|
||||
this.validator.validateForCreate(command.json());
|
||||
|
||||
final SmsMessage message = this.assembler.assembleFromJson(command);
|
||||
|
||||
// TODO: at this point we also want to fire off request using third party service to send SMS.
|
||||
// TODO: decision to be made on wheter we 'wait' for response or use 'future/promise' to capture response and update the SmsMessage table
|
||||
this.repository.save(message);
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(message.getId()) //
|
||||
.build();
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult update(final Long resourceId, final JsonCommand command) {
|
||||
|
||||
try {
|
||||
this.validator.validateForUpdate(command.json());
|
||||
|
||||
final SmsMessage message = this.assembler.assembleFromResourceId(resourceId);
|
||||
final Map<String, Object> changes = message.update(command);
|
||||
if (!changes.isEmpty()) {
|
||||
this.repository.save(message);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(resourceId) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult delete(final Long resourceId) {
|
||||
|
||||
try {
|
||||
final SmsMessage message = this.assembler.assembleFromResourceId(resourceId);
|
||||
this.repository.delete(message);
|
||||
this.repository.flush();
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleDataIntegrityIssues(null, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
return new CommandProcessingResultBuilder().withEntityId(resourceId).build();
|
||||
}
|
||||
|
||||
/*
|
||||
* Guaranteed to throw an exception no matter what the data integrity issue
|
||||
* is.
|
||||
*/
|
||||
private void handleDataIntegrityIssues(@SuppressWarnings("unused") final JsonCommand command, final DataIntegrityViolationException dve) {
|
||||
final Throwable realCause = dve.getMostSpecificCause();
|
||||
|
||||
if (realCause.getMessage().contains("mobile_no")) {
|
||||
throw new PlatformDataIntegrityException("error.msg.sms.no.mobile.no.exists", "The group, client or staff provided has no mobile no.", "id");
|
||||
}
|
||||
|
||||
logger.error(dve.getMessage(), dve);
|
||||
throw new PlatformDataIntegrityException("error.msg.sms.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource: " + realCause.getMessage());
|
||||
}
|
||||
}
|
||||
+18
-19
@@ -65,10 +65,9 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
@Column(name = "calculation_on_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal amountPercentageAppliedTo;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Column(name = "charge_amount_or_percentage", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amountOrPercentage;
|
||||
|
||||
|
||||
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal amount;
|
||||
|
||||
@@ -121,7 +120,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
BigDecimal loanCharge = BigDecimal.ZERO;
|
||||
if(ChargeTimeType.fromInt(chargeDefinition.getChargeTime()).equals(ChargeTimeType.INSTALMENT_FEE)){
|
||||
BigDecimal percentage = amount;
|
||||
@@ -134,7 +133,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return new LoanCharge(loan, chargeDefinition, amountPercentageAppliedTo, amount, chargeTime, chargeCalculation, dueDate,
|
||||
chargePaymentMode, null, loanCharge);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* loanPrincipal is required for charges that are percentage based
|
||||
*/
|
||||
@@ -150,7 +149,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
|
||||
public LoanCharge(final Loan loan, final Charge chargeDefinition, final BigDecimal loanPrincipal, final BigDecimal amount,
|
||||
final ChargeTimeType chargeTime, final ChargeCalculationType chargeCalculation, final LocalDate dueDate,
|
||||
final ChargePaymentMode chargePaymentMode, Integer numberOfRepayments, BigDecimal loanCharge) {
|
||||
final ChargePaymentMode chargePaymentMode, final Integer numberOfRepayments, final BigDecimal loanCharge) {
|
||||
this.loan = loan;
|
||||
this.charge = chargeDefinition;
|
||||
this.penaltyCharge = chargeDefinition.isPenalty();
|
||||
@@ -209,7 +208,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
this.percentage = null;
|
||||
this.amountPercentageAppliedTo = null;
|
||||
this.amountPaid = null;
|
||||
if(this.isInstalmentFee()){
|
||||
if(isInstalmentFee()){
|
||||
if(numberOfRepayments == null){
|
||||
numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments();
|
||||
}
|
||||
@@ -286,7 +285,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
if(this.isInstalmentFee()){
|
||||
if(isInstalmentFee()){
|
||||
if(numberOfRepayments == null){
|
||||
numberOfRepayments = this.loan.repaymentScheduleDetail().getNumberOfRepayments();
|
||||
}
|
||||
@@ -338,7 +337,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case INVALID:
|
||||
break;
|
||||
case FLAT:
|
||||
if(this.isInstalmentFee()){
|
||||
if(isInstalmentFee()){
|
||||
this.amount = newValue.multiply(BigDecimal.valueOf(this.loan.repaymentScheduleDetail().getNumberOfRepayments()));
|
||||
}else{
|
||||
this.amount = newValue;
|
||||
@@ -349,7 +348,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
case PERCENT_OF_INTEREST:
|
||||
this.percentage = newValue;
|
||||
this.amountPercentageAppliedTo = amount;
|
||||
BigDecimal loanCharge = loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(this.chargeCalculation),percentage);
|
||||
BigDecimal loanCharge = this.loan.calculatePerInstallmentChargeAmount(ChargeCalculationType.fromInt(this.chargeCalculation),this.percentage);
|
||||
if(loanCharge.compareTo(BigDecimal.ZERO) == 0){
|
||||
loanCharge = percentageOf(this.amountPercentageAppliedTo);
|
||||
}
|
||||
@@ -369,7 +368,7 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public boolean isSpecifiedDueDate() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.SPECIFIED_DUE_DATE);
|
||||
}
|
||||
|
||||
|
||||
public boolean isInstalmentFee() {
|
||||
return ChargeTimeType.fromInt(this.chargeTime).equals(ChargeTimeType.INSTALMENT_FEE);
|
||||
}
|
||||
@@ -424,8 +423,8 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
public BigDecimal percentageOf(final BigDecimal value) {
|
||||
return percentageOf(value, this.percentage);
|
||||
}
|
||||
|
||||
public static BigDecimal percentageOf(final BigDecimal value,BigDecimal percentage) {
|
||||
|
||||
public static BigDecimal percentageOf(final BigDecimal value,final BigDecimal percentage) {
|
||||
|
||||
BigDecimal percentageOf = BigDecimal.ZERO;
|
||||
|
||||
@@ -442,17 +441,17 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
* @returns a minimum cap or maximum cap set on charges if the criteria fits
|
||||
* else it returns the percentageOf if the amount is within min and max cap
|
||||
*/
|
||||
private BigDecimal minimumAndMaximumCap(BigDecimal percentageOf){
|
||||
private BigDecimal minimumAndMaximumCap(final BigDecimal percentageOf){
|
||||
BigDecimal minMaxCap = BigDecimal.ZERO;
|
||||
if(this.minCap !=null){
|
||||
int minimumCap = percentageOf.compareTo(minCap);
|
||||
final int minimumCap = percentageOf.compareTo(this.minCap);
|
||||
if(minimumCap == -1){
|
||||
minMaxCap = this.minCap;
|
||||
return minMaxCap;
|
||||
}
|
||||
}
|
||||
if(this.maxCap !=null){
|
||||
int maximumCap = percentageOf.compareTo(maxCap);
|
||||
final int maximumCap = percentageOf.compareTo(this.maxCap);
|
||||
if(maximumCap == 1) {
|
||||
minMaxCap = this.maxCap;
|
||||
return minMaxCap;
|
||||
@@ -607,18 +606,18 @@ public class LoanCharge extends AbstractPersistable<Long> {
|
||||
return ChargePaymentMode.fromInt(this.chargePaymentMode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public ChargeCalculationType getChargeCalculation() {
|
||||
return ChargeCalculationType.fromInt(this.chargeCalculation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BigDecimal getPercentage() {
|
||||
return this.percentage;
|
||||
}
|
||||
|
||||
|
||||
public void updateAmount(BigDecimal amount) {
|
||||
|
||||
public void updateAmount(final BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
calculateOutstanding();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user