From d7ea5d8169f103285faf1b11266da018c1495683 Mon Sep 17 00:00:00 2001 From: Keith Woodlock Date: Thu, 15 Nov 2012 21:58:19 +0000 Subject: [PATCH] MIFOSX-97: ability to support maker checker on create, update, delete of client. --- .../patch-0014-command-event-source-table.sql | 19 +++ .../platform/api/ClientsApiResource.java | 70 +++++----- ...Resource.java => CommandsApiResource.java} | 35 +++-- .../platform/api/commands/ClientCommand.java | 35 ++--- .../mifosng/platform/api/data/ClientData.java | 88 ++++++++----- .../platform/api/data/CommandSourceData.java | 39 ++++++ .../platform/api/data/EntityIdentifier.java | 4 + .../platform/api/data/MakerCheckerData.java | 31 ----- ...GsonPortfolioApiJsonSerializerService.java | 6 +- .../PortfolioApiDataConversionService.java | 2 +- ...PortfolioApiDataConversionServiceImpl.java | 29 +++-- .../PortfolioApiJsonSerializerService.java | 4 +- .../platform/client/domain/Client.java | 2 +- .../ClientReadPlatformServiceImpl.java | 2 +- .../service/ClientWritePlatformService.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 22 +++- ...ommandIsNotApprovedByCheckerException.java | 5 + .../platform/common/ApplicationConstants.java | 12 ++ .../infrastructure/api/JsonParserHelper.java | 8 +- .../{MakerChecker.java => CommandSource.java} | 67 +++++++--- ...tory.java => CommandSourceRepository.java} | 6 +- ...olioCommandSourceWritePlatformService.java | 12 ++ ...ortfolioCommandSourceWriteServiceImpl.java | 121 ++++++++++++++++++ .../PortfolioCommandsReadPlatformService.java | 13 ++ ...folioCommandsReadPlatformServiceImpl.java} | 29 +++-- ...tfolioMakerCheckerReadPlatformService.java | 13 -- ...PortfolioMakerCheckerWriteServiceImpl.java | 67 ---------- mifosng-provider/us.stackdump | 16 --- 28 files changed, 471 insertions(+), 290 deletions(-) create mode 100644 mifosng-db/patches/patch-0014-command-event-source-table.sql rename mifosng-provider/src/main/java/org/mifosng/platform/api/{MakerCheckerApiResource.java => CommandsApiResource.java} (70%) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/data/CommandSourceData.java delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/api/data/MakerCheckerData.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/client/service/RollbackTransactionAsCommandIsNotApprovedByCheckerException.java rename mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/{MakerChecker.java => CommandSource.java} (50%) rename mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/{MakerCheckerRepository.java => CommandSourceRepository.java} (61%) create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWritePlatformService.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWriteServiceImpl.java create mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformService.java rename mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/{PortfolioMakerCheckerReadPlatformServiceImpl.java => PortfolioCommandsReadPlatformServiceImpl.java} (51%) delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformService.java delete mode 100644 mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerWriteServiceImpl.java delete mode 100644 mifosng-provider/us.stackdump diff --git a/mifosng-db/patches/patch-0014-command-event-source-table.sql b/mifosng-db/patches/patch-0014-command-event-source-table.sql new file mode 100644 index 000000000..0db85e878 --- /dev/null +++ b/mifosng-db/patches/patch-0014-command-event-source-table.sql @@ -0,0 +1,19 @@ +drop table `m_maker_checker`; +drop table `m_command_event_source`; +drop table `m_portfolio_command_source`; +CREATE TABLE `m_portfolio_command_source` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `api_operation` varchar(20) NOT NULL, + `api_resource` varchar(20) NOT NULL, + `resource_id` bigint(20) DEFAULT NULL, + `command_as_json` varchar(1000) NOT NULL, + `maker_id` bigint(20) NOT NULL, + `made_on_date` date NOT NULL, + `checker_id` bigint(20) DEFAULT NULL, + `checked_on_date` date DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `FK_m_maker_m_appuser` (`maker_id`), + KEY `FK_m_checker_m_appuser` (`checker_id`), + CONSTRAINT `FK_m_maker_m_appuser` FOREIGN KEY (`maker_id`) REFERENCES `m_appuser` (`id`), + CONSTRAINT `FK_m_checker_m_appuser` FOREIGN KEY (`checker_id`) REFERENCES `m_appuser` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/ClientsApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/ClientsApiResource.java index f22051bd0..c22bdf69a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/ClientsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/ClientsApiResource.java @@ -28,8 +28,8 @@ import org.mifosng.platform.api.commands.ClientCommand; import org.mifosng.platform.api.commands.NoteCommand; import org.mifosng.platform.api.data.ClientAccountSummaryCollectionData; import org.mifosng.platform.api.data.ClientData; +import org.mifosng.platform.api.data.CommandSourceData; import org.mifosng.platform.api.data.EntityIdentifier; -import org.mifosng.platform.api.data.MakerCheckerData; import org.mifosng.platform.api.data.NoteData; import org.mifosng.platform.api.data.OfficeLookup; import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService; @@ -42,8 +42,8 @@ import org.mifosng.platform.common.FileUtils; import org.mifosng.platform.exceptions.ClientNotFoundException; import org.mifosng.platform.exceptions.ImageNotFoundException; import org.mifosng.platform.infrastructure.api.ApiParameterHelper; -import org.mifosng.platform.makerchecker.service.PortfolioMakerCheckerReadPlatformService; -import org.mifosng.platform.makerchecker.service.PortfolioMakerCheckerWriteService; +import org.mifosng.platform.makerchecker.service.PortfolioCommandSourceWritePlatformService; +import org.mifosng.platform.makerchecker.service.PortfolioCommandsReadPlatformService; import org.mifosng.platform.organisation.service.OfficeReadPlatformService; import org.mifosng.platform.security.PlatformSecurityContext; import org.slf4j.Logger; @@ -69,22 +69,22 @@ public class ClientsApiResource { @Autowired private ClientWritePlatformService clientWritePlatformService; + + @Autowired + private PortfolioCommandsReadPlatformService commandSourceReadPlatformService; + + @Autowired + private PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + private OfficeReadPlatformService officeReadPlatformService; @Autowired private PortfolioApiDataConversionService apiDataConversionService; - @Autowired - private PortfolioMakerCheckerWriteService portfolioMakerCheckerService; - - @Autowired - private OfficeReadPlatformService officeReadPlatformService; - @Autowired private PortfolioApiJsonSerializerService apiJsonSerializerService; - @Autowired - private PortfolioMakerCheckerReadPlatformService makerCheckerReadPlatformService; - @Autowired private PlatformSecurityContext context; @@ -169,12 +169,19 @@ public class ClientsApiResource { final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); final boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters()); + final Long makerCheckerId = ApiParameterHelper.makerCheckerId(uriInfo.getQueryParameters()); ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId); if (template) { final List allowedOffices = new ArrayList(officeReadPlatformService.retrieveAllOfficesForLookup()); clientData = ClientData.templateOnTop(clientData, allowedOffices); } + if (makerCheckerId != null) { + final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(makerCheckerId); + final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, entry.json(), false); + + clientData = ClientData.integrateChanges(clientData, command); + } return this.apiJsonSerializerService.serializeClientDataToJson(prettyPrint, responseParameters, clientData); } @@ -193,10 +200,10 @@ public class ClientsApiResource { ClientData clientData = this.clientReadPlatformService.retrieveNewClientDetails(); if (makerCheckerId != null) { - final MakerCheckerData entry = this.makerCheckerReadPlatformService.retrieveById(makerCheckerId); - final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, entry.json()); + final CommandSourceData entry = this.commandSourceReadPlatformService.retrieveById(makerCheckerId); + final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, entry.json(), false); - clientData = ClientData.mergeWithCommand(clientData, command); + clientData = ClientData.integrateChanges(clientData, command); } return this.apiJsonSerializerService.serializeClientDataToJson(prettyPrint, responseParameters, clientData); @@ -205,24 +212,11 @@ public class ClientsApiResource { @POST @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) - public String enrollClient(final String jsonRequestBody) { + public String createClient(final String jsonRequestBody) { - final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, jsonRequestBody); - command.validateForCreate(); + final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("CREATE", "clients", null, jsonRequestBody); - String jsonResult = ""; - // hardcode to false before checking in until configuration for enabling is done. - final boolean makerCheckerEnabledForTask = false; - if (makerCheckerEnabledForTask) { - final Long makerCheckerId = this.portfolioMakerCheckerService.logNewEntry("CREATE_CLIENT", jsonRequestBody); - jsonResult = this.apiJsonSerializerService.serializeEntityIdentifier(EntityIdentifier.makerChecker(makerCheckerId)); - } else { - final Long clientId = this.clientWritePlatformService.enrollClient(command); - - jsonResult = this.apiJsonSerializerService.serializeEntityIdentifier(new EntityIdentifier(clientId)); - } - - return jsonResult; + return this.apiJsonSerializerService.serializeEntityIdentifier(result); } @PUT @@ -231,11 +225,9 @@ public class ClientsApiResource { @Produces({MediaType.APPLICATION_JSON}) public String updateClient(@PathParam("clientId") final Long clientId, final String jsonRequestBody) { - final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(clientId, jsonRequestBody); - - final EntityIdentifier identifier = this.clientWritePlatformService.updateClientDetails(command); - - return this.apiJsonSerializerService.serializeEntityIdentifier(identifier); + final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", "clients", clientId, jsonRequestBody); + + return this.apiJsonSerializerService.serializeEntityIdentifier(result); } @DELETE @@ -244,9 +236,9 @@ public class ClientsApiResource { @Produces({MediaType.APPLICATION_JSON}) public String deleteClient(@PathParam("clientId") final Long clientId) { - this.clientWritePlatformService.deleteClient(clientId); - - return this.apiJsonSerializerService.serializeEntityIdentifier(new EntityIdentifier(clientId)); + final EntityIdentifier result = this.commandsSourceWritePlatformService.logCommandSource("DELETE", "clients", clientId, "{}"); + + return this.apiJsonSerializerService.serializeEntityIdentifier(result); } @GET diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/MakerCheckerApiResource.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/CommandsApiResource.java similarity index 70% rename from mifosng-provider/src/main/java/org/mifosng/platform/api/MakerCheckerApiResource.java rename to mifosng-provider/src/main/java/org/mifosng/platform/api/CommandsApiResource.java index d167acfe5..40f4abeba 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/MakerCheckerApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/CommandsApiResource.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.Set; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; @@ -16,27 +17,27 @@ import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; import org.mifosng.platform.api.data.EntityIdentifier; -import org.mifosng.platform.api.data.MakerCheckerData; +import org.mifosng.platform.api.data.CommandSourceData; import org.mifosng.platform.api.infrastructure.PortfolioApiJsonSerializerService; import org.mifosng.platform.exceptions.UnrecognizedQueryParamException; import org.mifosng.platform.infrastructure.api.ApiParameterHelper; -import org.mifosng.platform.makerchecker.service.PortfolioMakerCheckerReadPlatformService; -import org.mifosng.platform.makerchecker.service.PortfolioMakerCheckerWriteService; import org.mifosng.platform.security.PlatformSecurityContext; +import org.mifosng.platform.makerchecker.service.PortfolioCommandsReadPlatformService; +import org.mifosng.platform.makerchecker.service.PortfolioCommandSourceWritePlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -@Path("/makerchecker") +@Path("/commands") @Component @Scope("singleton") -public class MakerCheckerApiResource { +public class CommandsApiResource { @Autowired - private PortfolioMakerCheckerReadPlatformService readPlatformService; + private PortfolioCommandsReadPlatformService readPlatformService; @Autowired - private PortfolioMakerCheckerWriteService writePlatformService; + private PortfolioCommandSourceWritePlatformService writePlatformService; @Autowired private PortfolioApiJsonSerializerService apiJsonSerializerService; @@ -55,7 +56,7 @@ public class MakerCheckerApiResource { final Set responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters()); final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); - final Collection entries = this.readPlatformService.retrieveAllEntriesToBeChecked(); + final Collection entries = this.readPlatformService.retrieveAllEntriesToBeChecked(); return this.apiJsonSerializerService.serializeMakerCheckerDataToJson(prettyPrint, responseParameters, entries); } @@ -68,18 +69,30 @@ public class MakerCheckerApiResource { @PathParam("makerCheckerId") final Long makerCheckerId, @QueryParam("command") final String commandParam) { - Long id = null; + EntityIdentifier result = null; if (is(commandParam, "approve")) { - id = this.writePlatformService.approveEntry(makerCheckerId); + result = this.writePlatformService.approveEntry(makerCheckerId); } else { throw new UnrecognizedQueryParamException("command", commandParam); } - return this.apiJsonSerializerService.serializeEntityIdentifier(EntityIdentifier.makerChecker(id)); + return this.apiJsonSerializerService.serializeEntityIdentifier(result); } private boolean is(final String commandParam, final String commandValue) { return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue); } + + @DELETE + @Path("{makerCheckerId}") + @Consumes({MediaType.APPLICATION_JSON}) + @Produces({MediaType.APPLICATION_JSON}) + public String deleteMakerCheckerEntry( + @PathParam("makerCheckerId") final Long makerCheckerId) { + + final Long id = this.writePlatformService.deleteEntry(makerCheckerId); + + return this.apiJsonSerializerService.serializeEntityIdentifier(EntityIdentifier.makerChecker(id)); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ClientCommand.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ClientCommand.java index 070f97780..625e0df0a 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ClientCommand.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/commands/ClientCommand.java @@ -3,8 +3,10 @@ package org.mifosng.platform.api.commands; import java.util.Set; import org.joda.time.LocalDate; -import org.mifosng.platform.client.service.ClientCommandValidator; +/** + * + */ public class ClientCommand { private final Long id; @@ -14,11 +16,11 @@ public class ClientCommand { private final String clientOrBusinessName; private final Long officeId; private final LocalDate joiningDate; - - private final Set modifiedParameters; - - private final ClientCommandValidator validator; + // made transient as dont want them to be serialized/converted to JSON + private final transient boolean makerCheckerApproval; + private final transient Set parametersPassedInRequest; + public ClientCommand( final Set modifiedParameters, final Long id, @@ -27,8 +29,9 @@ public class ClientCommand { final String lastname, final String clientOrBusinessName, final Long officeId, - final LocalDate joiningDate) { - this.modifiedParameters = modifiedParameters; + final LocalDate joiningDate, + final boolean makerCheckerApproval) { + this.parametersPassedInRequest = modifiedParameters; this.id = id; this.externalId = externalId; this.firstname = firstname; @@ -36,7 +39,7 @@ public class ClientCommand { this.clientOrBusinessName = clientOrBusinessName; this.officeId = officeId; this.joiningDate = joiningDate; - this.validator = new ClientCommandValidator(this); + this.makerCheckerApproval = makerCheckerApproval; } public Long getId() { @@ -68,30 +71,30 @@ public class ClientCommand { } public boolean isFirstnameChanged() { - return this.modifiedParameters.contains("firstname"); + return this.parametersPassedInRequest.contains("firstname"); } public boolean isLastnameChanged() { - return this.modifiedParameters.contains("lastname"); + return this.parametersPassedInRequest.contains("lastname"); } public boolean isClientOrBusinessNameChanged() { - return this.modifiedParameters.contains("clientOrBusinessName"); + return this.parametersPassedInRequest.contains("clientOrBusinessName"); } public boolean isExternalIdChanged() { - return this.modifiedParameters.contains("externalId"); + return this.parametersPassedInRequest.contains("externalId"); } public boolean isJoiningDateChanged() { - return this.modifiedParameters.contains("joiningDate"); + return this.parametersPassedInRequest.contains("joiningDate"); } public boolean isOfficeChanged() { - return this.modifiedParameters.contains("officeId"); + return this.parametersPassedInRequest.contains("officeId"); } - public void validateForCreate() { - this.validator.validateForCreate(); + public boolean isApprovedByChecker() { + return this.makerCheckerApproval; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientData.java index 35606afe4..9686c8ef8 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/ClientData.java @@ -2,7 +2,6 @@ package org.mifosng.platform.api.data; import java.util.List; -import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.mifosng.platform.api.commands.ClientCommand; @@ -24,27 +23,53 @@ final public class ClientData { private final String imageKey; @SuppressWarnings("unused") private final boolean imagePresent; + private final List allowedOffices; - // essential override existing data with data from command - public static ClientData mergeWithCommand(final ClientData clientData, final ClientCommand command) { - - final Long officeId = (Long) ObjectUtils.defaultIfNull(command.getId(), clientData.officeId); - final String firstname = (String) ObjectUtils.defaultIfNull(command.getFirstname(), clientData.firstname); - final String lastname = (String) ObjectUtils.defaultIfNull(command.getLastname(), clientData.lastname); - final String clientOrBusinessName = (String) ObjectUtils.defaultIfNull(command.getClientOrBusinessName(), clientData.clientOrBusinessName); - final String externalId = (String) ObjectUtils.defaultIfNull(command.getExternalId(), clientData.externalId); - final LocalDate joinedDate = (LocalDate) ObjectUtils.defaultIfNull(command.getJoiningDate(), clientData.joinedDate); - - return new ClientData(officeId, clientData.officeName, clientData.id, firstname, lastname, clientOrBusinessName, externalId, joinedDate, clientData.imageKey, clientData.allowedOffices); + @SuppressWarnings("unused") + private final ClientCommand changes; + + private static String buildDisplayNameFrom(final String firstname, final String lastname) { + String displayName = null; + StringBuilder displayNameBuilder = new StringBuilder(); + if (StringUtils.isNotBlank(firstname)) { + displayNameBuilder.append(firstname).append(' '); + } + + if (StringUtils.isNotBlank(lastname)) { + displayNameBuilder.append(lastname); + displayName = displayNameBuilder.toString(); + } + return displayName; + } + + public static ClientData integrateChanges(final ClientData clientData, final ClientCommand command) { + String firstname = clientData.firstname; + String lastname = clientData.lastname; + if (StringUtils.isNotBlank(clientData.clientOrBusinessName)) { + firstname = null; + lastname = clientData.clientOrBusinessName; + } + final String displayName = buildDisplayNameFrom(firstname, lastname); + return new ClientData(clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName, + clientData.externalId, clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, command); } public static ClientData template(final Long officeId, final LocalDate joinedDate, final List allowedOffices) { - return new ClientData(officeId, null, null, null, null, null, null, joinedDate, null, allowedOffices); + return new ClientData(officeId, null, null, null, null, null, null, joinedDate, null, allowedOffices, null); } public static ClientData templateOnTop(final ClientData clientData, final List allowedOffices) { - return new ClientData(clientData.officeId, clientData.officeName, clientData.id, clientData.firstname, clientData.lastname, clientData.clientOrBusinessName, clientData.externalId, clientData.joinedDate, clientData.imageKey, allowedOffices); + + String firstname = clientData.firstname; + String lastname = clientData.lastname; + if (StringUtils.isNotBlank(clientData.clientOrBusinessName)) { + firstname = null; + lastname = clientData.clientOrBusinessName; + } + final String displayName = buildDisplayNameFrom(firstname, lastname); + return new ClientData(clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName, + clientData.externalId, clientData.joinedDate, clientData.imageKey, allowedOffices, null); } public static ClientData clientIdentifier( @@ -54,46 +79,43 @@ final public class ClientData { final Long officeId, final String officeName) { - StringBuilder displayNameBuilder = new StringBuilder(firstname); - if (StringUtils.isNotBlank(displayNameBuilder.toString())) { - displayNameBuilder.append(' '); - } - displayNameBuilder.append(lastname); + final String displayName = buildDisplayNameFrom(firstname, lastname); - return new ClientData(officeId, officeName, id, firstname, lastname, displayNameBuilder.toString(), null, null, null, null); + return new ClientData(officeId, officeName, id, firstname, lastname, displayName, null, null, null, null, null); } - public ClientData(final Long officeId, final String officeName, - final Long id, final String firstname, final String lastname, - final String displayName, final String externalId, + public ClientData( + final Long officeId, + final String officeName, + final Long id, + final String firstname, + final String lastname, + final String displayName, + final String externalId, final LocalDate joinedDate, final String imageKey, - final List allowedOffices) { + final List allowedOffices, + final ClientCommand changes) { this.officeId = officeId; this.officeName = officeName; this.id = id; this.firstname = firstname; - this.displayName = displayName; /*** unset last name for business name **/ - if (StringUtils.isBlank(this.firstname)) { + if (StringUtils.isBlank(firstname)) { this.lastname = null; this.clientOrBusinessName = lastname; } else { this.lastname = lastname; this.clientOrBusinessName = null; } - + this.displayName = displayName; this.externalId = externalId; this.joinedDate = joinedDate; this.imageKey = imageKey; - if (imageKey == null) { - imagePresent = false; - }else{ - imagePresent = true; - } - + this.imagePresent = (imageKey != null); this.allowedOffices = allowedOffices; + this.changes = changes; } public String displayName() { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/CommandSourceData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/CommandSourceData.java new file mode 100644 index 000000000..63c46ce05 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/CommandSourceData.java @@ -0,0 +1,39 @@ +package org.mifosng.platform.api.data; + +import org.joda.time.LocalDate; + +/** + * Immutable data object representing maker-checker entry + */ +final public class CommandSourceData { + + @SuppressWarnings("unused") + private Long id; + + @SuppressWarnings("unused") + private String apiOperation; + + @SuppressWarnings("unused") + private String resource; + + @SuppressWarnings("unused") + private final Long resourceId; + + private String commandJson; + + @SuppressWarnings("unused") + private final LocalDate madeOnDate; + + public CommandSourceData(final Long id, final String apiOperation, final String resource, final Long resourceId, final String commandJson, final LocalDate madeOnDate) { + this.id = id; + this.apiOperation = apiOperation; + this.resource = resource; + this.resourceId = resourceId; + this.commandJson = commandJson; + this.madeOnDate = madeOnDate; + } + + public String json() { + return this.commandJson; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/EntityIdentifier.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/EntityIdentifier.java index e1f74e41c..8af2bc21c 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/EntityIdentifier.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/EntityIdentifier.java @@ -13,6 +13,10 @@ public class EntityIdentifier { return new EntityIdentifier(null, makerCheckerId); } + public static EntityIdentifier makerChecker(final Long resourceId, final Long makerCheckerId) { + return new EntityIdentifier(resourceId, makerCheckerId); + } + public EntityIdentifier() { // } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/MakerCheckerData.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/data/MakerCheckerData.java deleted file mode 100644 index 8e1936deb..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/data/MakerCheckerData.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.mifosng.platform.api.data; - -import org.joda.time.LocalDate; - -/** - * Immutable data object representing maker-checker entry - */ -final public class MakerCheckerData { - - @SuppressWarnings("unused") - private Long id; - - @SuppressWarnings("unused") - private String taskName; - - private String taskJson; - - @SuppressWarnings("unused") - private final LocalDate madeOnDate; - - public MakerCheckerData(final Long id, final String taskName, final String taskJson, final LocalDate madeOnDate) { - this.id = id; - this.taskName = taskName; - this.taskJson = taskJson; - this.madeOnDate = madeOnDate; - } - - public String json() { - return this.taskJson; - } -} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java index 194fc6cda..6ace67acb 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/GoogleGsonPortfolioApiJsonSerializerService.java @@ -30,7 +30,7 @@ import org.mifosng.platform.api.data.LoanChargeData; import org.mifosng.platform.api.data.LoanProductData; import org.mifosng.platform.api.data.LoanReassignmentData; import org.mifosng.platform.api.data.LoanTransactionData; -import org.mifosng.platform.api.data.MakerCheckerData; +import org.mifosng.platform.api.data.CommandSourceData; import org.mifosng.platform.api.data.NoteData; import org.mifosng.platform.api.data.OfficeData; import org.mifosng.platform.api.data.OfficeTransactionData; @@ -729,12 +729,12 @@ public class GoogleGsonPortfolioApiJsonSerializerService implements PortfolioApi public String serializeMakerCheckerDataToJson( final boolean prettyPrint, final Set responseParameters, - final Collection entries) { + final Collection entries) { final Gson gsonDeserializer = helper .createGsonBuilderWithParameterExclusionSerializationStrategy( MAKER_CHECKER_DATA_PARAMETERS, prettyPrint, responseParameters); return helper.serializedJsonFrom(gsonDeserializer, - entries.toArray(new MakerCheckerData[entries.size()])); + entries.toArray(new CommandSourceData[entries.size()])); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java index 015ff1b65..bb9c48b78 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionService.java @@ -49,7 +49,7 @@ public interface PortfolioApiDataConversionService { DepositProductCommand convertJsonToDepositProductCommand(Long resourceIdentifier, String json); - ClientCommand convertJsonToClientCommand(Long resourceIdentifier, String json); + ClientCommand convertJsonToClientCommand(Long resourceIdentifier, String json, boolean makerCheckerApproval); GroupCommand convertJsonToGroupCommand(Long resourceIdentifier, String json); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java index 6bb8da4ea..b61b2651e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiDataConversionServiceImpl.java @@ -351,30 +351,33 @@ public class PortfolioApiDataConversionServiceImpl implements PortfolioApiDataCo } @Override - public ClientCommand convertJsonToClientCommand(final Long resourceIdentifier, final String json) { + public ClientCommand convertJsonToClientCommand(final Long resourceIdentifier, final String json, final boolean makerCheckerApproval) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } - Type typeOfMap = new TypeToken>(){}.getType(); - Map requestMap = gsonConverter.fromJson(json, typeOfMap); + final Type typeOfMap = new TypeToken>(){}.getType(); + final Map requestMap = gsonConverter.fromJson(json, typeOfMap); - Set supportedParams = new HashSet( + final Set supportedParams = new HashSet( Arrays.asList("externalId", "firstname", "lastname", "clientOrBusinessName", "officeId", "joiningDate", "locale", "dateFormat") ); checkForUnsupportedParameters(requestMap, supportedParams); - Set modifiedParameters = new HashSet(); - - String externalId = extractStringParameter("externalId", requestMap, modifiedParameters); - Long officeId = extractLongParameter("officeId", requestMap, modifiedParameters); - LocalDate joiningDate = extractLocalDateParameter("joiningDate", requestMap, modifiedParameters); - String firstname = extractStringParameter("firstname", requestMap, modifiedParameters); - String lastname = extractStringParameter("lastname", requestMap, modifiedParameters); - String clientOrBusinessName = extractStringParameter("clientOrBusinessName", requestMap, modifiedParameters); + final JsonParser parser = new JsonParser(); + final JsonElement element = parser.parse(json); + final JsonParserHelper helper = new JsonParserHelper(); + final Set parametersPassedInRequest = new HashSet(); - return new ClientCommand(modifiedParameters, resourceIdentifier, externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate); + final Long officeId = helper.extractLongNamed("officeId", element, parametersPassedInRequest); + final String externalId = helper.extractStringNamed("externalId", element, parametersPassedInRequest); + final String firstname = helper.extractStringNamed("firstname", element, parametersPassedInRequest); + final String lastname = helper.extractStringNamed("lastname", element, parametersPassedInRequest); + final String clientOrBusinessName = helper.extractStringNamed("clientOrBusinessName", element, parametersPassedInRequest); + final LocalDate joiningDate = helper.extractLocalDateNamed("joiningDate", element, parametersPassedInRequest); + + return new ClientCommand(parametersPassedInRequest, resourceIdentifier, externalId, firstname, lastname, clientOrBusinessName, officeId, joiningDate, makerCheckerApproval); } @Override diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java index 39f38c347..acb164d0e 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/api/infrastructure/PortfolioApiJsonSerializerService.java @@ -28,7 +28,7 @@ import org.mifosng.platform.api.data.LoanChargeData; import org.mifosng.platform.api.data.LoanProductData; import org.mifosng.platform.api.data.LoanReassignmentData; import org.mifosng.platform.api.data.LoanTransactionData; -import org.mifosng.platform.api.data.MakerCheckerData; +import org.mifosng.platform.api.data.CommandSourceData; import org.mifosng.platform.api.data.NoteData; import org.mifosng.platform.api.data.OfficeData; import org.mifosng.platform.api.data.OfficeTransactionData; @@ -136,7 +136,7 @@ public interface PortfolioApiJsonSerializerService { String serializeCodeDataToJson(boolean prettyPrint,Set responseParameters, CodeData code); - String serializeMakerCheckerDataToJson(boolean prettyPrint,Set responseParameters, Collection entries); + String serializeMakerCheckerDataToJson(boolean prettyPrint,Set responseParameters, Collection entries); String serializeSavingAccountsDataToJson(boolean prettyPrint, Set responseParameters, SavingAccountData account); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java index 5b1bb95f9..c9e582dfe 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/domain/Client.java @@ -133,7 +133,7 @@ public class Client extends AbstractAuditableCustom { } if (command.isExternalIdChanged()) { - this.externalId = StringUtils.defaultIfEmpty(command.getExternalId().trim(), null); + this.externalId = StringUtils.defaultIfEmpty(command.getExternalId(), null); } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java index f74e83868..1315aac1f 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientReadPlatformServiceImpl.java @@ -164,7 +164,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String officeName = rs.getString("officeName"); return new ClientData(officeId, officeName, id, firstname, - lastname, displayName, externalId, joinedDate,imageKey, null); + lastname, displayName, externalId, joinedDate,imageKey, null, null); } } diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java index cd306b6d0..85153cfcf 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformService.java @@ -12,13 +12,13 @@ import org.springframework.security.access.prepost.PreAuthorize; public interface ClientWritePlatformService { @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_CLIENT')") - Long enrollClient(ClientCommand command); + Long createClient(ClientCommand command); @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'UPDATE_CLIENT')") EntityIdentifier updateClientDetails(ClientCommand command); @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'DELETE_CLIENT')") - EntityIdentifier deleteClient(Long clientId); + EntityIdentifier deleteClient(ClientCommand command); @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'PORTFOLIO_MANAGEMENT_SUPER_USER', 'CREATE_CLIENTIMAGE')") EntityIdentifier saveOrUpdateClientImage(Long clientId, String imageName, InputStream inputStream); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 7e4fa8cd3..512963f79 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -50,6 +50,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final NoteRepository noteRepository; private final CodeValueRepository codeValueRepository; + // FIXME - kw - this value is hardcoded for now but will be retrieved from coinfiguration + private boolean makerCheckerEnabledForTask = false; + @Autowired public ClientWritePlatformServiceJpaRepositoryImpl( final PlatformSecurityContext context, @@ -68,8 +71,9 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP @Transactional @Override - public EntityIdentifier deleteClient(final Long clientId) { + public EntityIdentifier deleteClient(final ClientCommand command) { + final Long clientId = command.getId(); final Client client = this.clientRepository.findOne(clientId); if (client == null || client.isDeleted()) { throw new ClientNotFoundException(clientId); @@ -77,6 +81,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP client.delete(); this.clientRepository.save(client); + + if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { + throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); + } return new EntityIdentifier(client.getId()); } @@ -105,7 +113,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP @Transactional @Override - public Long enrollClient(final ClientCommand command) { + public Long createClient(final ClientCommand command) { try { context.authenticatedUser(); @@ -125,12 +133,16 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP firstname = null; } - Client newClient = Client + final Client newClient = Client .newClient(clientOffice, firstname, lastname, command.getJoiningDate(), command.getExternalId()); this.clientRepository.save(newClient); + if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { + throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); + } + return newClient.getId(); } catch (DataIntegrityViolationException dve) { handleDataIntegrityIssues(command, dve); @@ -164,6 +176,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP clientForUpdate.update(clientOffice, command); this.clientRepository.saveAndFlush(clientForUpdate); + + if (this.makerCheckerEnabledForTask && !command.isApprovedByChecker()) { + throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(); + } return new EntityIdentifier(clientForUpdate.getId()); } catch (DataIntegrityViolationException dve) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/client/service/RollbackTransactionAsCommandIsNotApprovedByCheckerException.java b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/RollbackTransactionAsCommandIsNotApprovedByCheckerException.java new file mode 100644 index 000000000..17a9f206b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/client/service/RollbackTransactionAsCommandIsNotApprovedByCheckerException.java @@ -0,0 +1,5 @@ +package org.mifosng.platform.client.service; + +public class RollbackTransactionAsCommandIsNotApprovedByCheckerException extends RuntimeException { + +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/common/ApplicationConstants.java b/mifosng-provider/src/main/java/org/mifosng/platform/common/ApplicationConstants.java index 161b482a1..b1f4a0151 100755 --- a/mifosng-provider/src/main/java/org/mifosng/platform/common/ApplicationConstants.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/common/ApplicationConstants.java @@ -1,7 +1,19 @@ package org.mifosng.platform.common; +/** + * @deprecated - KW - in general would like to avoid the whole throw it in common approach and dont want to see + * - this file attract more application constants. + */ +@Deprecated public class ApplicationConstants { + /** + * The intention of m_code table usage is not to use the id but the unique name of the code. + * + * This will stop reserved magic numbers spreading through application for one and it + * more natural to use the code name like you would use a table name in a query. + */ + @Deprecated public static final Long CLIENT_IDENTITY_CODE_ID = 9L; // TODO:Vishwas Need to move these settings to the Database diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/api/JsonParserHelper.java b/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/api/JsonParserHelper.java index cde4ffbfe..6c4b76947 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/api/JsonParserHelper.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/infrastructure/api/JsonParserHelper.java @@ -42,13 +42,13 @@ public class JsonParserHelper { } public Long extractLongNamed(final String parameterName, - final JsonElement element, final Set modifiedParameters) { + final JsonElement element, final Set parametersPassedInRequest) { Long longValue = null; if (element.isJsonObject()) { JsonObject object = element.getAsJsonObject(); if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) { - modifiedParameters.add(parameterName); + parametersPassedInRequest.add(parameterName); JsonPrimitive primitive = object.get(parameterName) .getAsJsonPrimitive(); final String stringValue = primitive.getAsString(); @@ -60,12 +60,12 @@ public class JsonParserHelper { return longValue; } - public String extractStringNamed(final String parameterName, final JsonElement element, final Set modifiedParameters) { + public String extractStringNamed(final String parameterName, final JsonElement element, final Set parametersPassedInRequest) { String stringValue = null; if (element.isJsonObject()) { JsonObject object = element.getAsJsonObject(); if (object.has(parameterName) && object.get(parameterName).isJsonPrimitive()) { - modifiedParameters.add(parameterName); + parametersPassedInRequest.add(parameterName); final JsonPrimitive primitive = object.get(parameterName).getAsJsonPrimitive(); final String valueAsString = primitive.getAsString(); if (StringUtils.isNotBlank(valueAsString)) { diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/MakerChecker.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/CommandSource.java similarity index 50% rename from mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/MakerChecker.java rename to mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/CommandSource.java index f30d50eb4..05faf1c0b 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/MakerChecker.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/CommandSource.java @@ -16,15 +16,20 @@ import org.mifosng.platform.user.domain.AppUser; import org.springframework.data.jpa.domain.AbstractPersistable; @Entity -@Table(name = "m_maker_checker") -public class MakerChecker extends AbstractPersistable { +@Table(name = "m_portfolio_command_source") +public class CommandSource extends AbstractPersistable { - @SuppressWarnings("unused") - @Column(name = "task_name", length = 100) - private String taskName; - - @Column(name = "task_json", length = 1000) - private String taskJson; + @Column(name = "api_operation", length = 20) + private String apiOperation; + + @Column(name = "api_resource", length = 20) + private String resource; + + @Column(name = "resource_id") + private Long resourceId; + + @Column(name = "command_as_json", length = 1000) + private String commandAsJson; // should maker and checker be a staff member as opposed to just any application user? @SuppressWarnings("unused") @@ -47,25 +52,31 @@ public class MakerChecker extends AbstractPersistable { @Temporal(TemporalType.DATE) private Date checkedOnDate; - public static MakerChecker makerEntry( - final String taskName, + public static CommandSource createdBy( + final String apiOperation, + final String resource, + final Long resourceId, final String jsonRequestBody, final AppUser maker, final LocalDate madeOnDate) { - return new MakerChecker(taskName, jsonRequestBody, maker, madeOnDate); + return new CommandSource(apiOperation, resource, resourceId, jsonRequestBody, maker, madeOnDate); } - protected MakerChecker() { + protected CommandSource() { // } - private MakerChecker( - final String taskName, - final String taskJson, + private CommandSource( + final String apiOperation, + final String resource, + final Long resourceId, + final String jsonRequestBody, final AppUser maker, final LocalDate madeOnDate) { - this.taskName = StringUtils.defaultIfEmpty(taskName, null); - this.taskJson = StringUtils.defaultIfEmpty(taskJson, null); + this.apiOperation = StringUtils.defaultIfEmpty(apiOperation, null); + this.resource = StringUtils.defaultIfEmpty(resource, null); + this.resourceId = resourceId; + this.commandAsJson = StringUtils.defaultIfEmpty(jsonRequestBody, null); this.maker = maker; this.madeOnDate = madeOnDate.toDate(); } @@ -74,8 +85,28 @@ public class MakerChecker extends AbstractPersistable { this.checker = checker; this.checkedOnDate = checkedOnDate.toDate(); } + + public Long resourceId() { + return this.resourceId; + } public String json() { - return this.taskJson; + return this.commandAsJson; + } + + public boolean isClientResource() { + return this.resource.equalsIgnoreCase("CLIENTS"); + } + + public boolean isCreate() { + return this.apiOperation.equalsIgnoreCase("CREATE"); + } + + public boolean isUpdate() { + return this.apiOperation.equalsIgnoreCase("UPDATE") && this.resourceId != null; + } + + public boolean isDelete() { + return this.apiOperation.equalsIgnoreCase("DELETE") && this.resourceId != null; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/MakerCheckerRepository.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/CommandSourceRepository.java similarity index 61% rename from mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/MakerCheckerRepository.java rename to mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/CommandSourceRepository.java index e8a59475e..d0446fce8 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/MakerCheckerRepository.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/domain/CommandSourceRepository.java @@ -3,8 +3,8 @@ package org.mifosng.platform.makerchecker.domain; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface MakerCheckerRepository extends - JpaRepository, - JpaSpecificationExecutor { +public interface CommandSourceRepository extends + JpaRepository, + JpaSpecificationExecutor { // no added behaviour } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWritePlatformService.java new file mode 100644 index 000000000..61ebed4ba --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWritePlatformService.java @@ -0,0 +1,12 @@ +package org.mifosng.platform.makerchecker.service; + +import org.mifosng.platform.api.data.EntityIdentifier; + +public interface PortfolioCommandSourceWritePlatformService { + + EntityIdentifier logCommandSource(String apiOperation, String resource, Long resourceId, String jsonRequestBody); + + EntityIdentifier approveEntry(Long id); + + Long deleteEntry(Long makerCheckerId); +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWriteServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWriteServiceImpl.java new file mode 100644 index 000000000..664e51c6a --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandSourceWriteServiceImpl.java @@ -0,0 +1,121 @@ +package org.mifosng.platform.makerchecker.service; + +import org.joda.time.LocalDate; +import org.mifosng.platform.api.commands.ClientCommand; +import org.mifosng.platform.api.data.EntityIdentifier; +import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService; +import org.mifosng.platform.client.service.ClientWritePlatformService; +import org.mifosng.platform.client.service.RollbackTransactionAsCommandIsNotApprovedByCheckerException; +import org.mifosng.platform.makerchecker.domain.CommandSource; +import org.mifosng.platform.makerchecker.domain.CommandSourceRepository; +import org.mifosng.platform.security.PlatformSecurityContext; +import org.mifosng.platform.user.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * + */ +@Service +public class PortfolioCommandSourceWriteServiceImpl implements PortfolioCommandSourceWritePlatformService { + + private final PlatformSecurityContext context; + private final CommandSourceRepository commandSourceRepository; + private final PortfolioApiDataConversionService apiDataConversionService; + private final ClientWritePlatformService clientWritePlatformService; + + @Autowired + public PortfolioCommandSourceWriteServiceImpl(final PlatformSecurityContext context, final CommandSourceRepository makerCheckerRepository, + final PortfolioApiDataConversionService apiDataConversionService, + final ClientWritePlatformService clientWritePlatformService) { + this.context = context; + this.commandSourceRepository = makerCheckerRepository; + this.apiDataConversionService = apiDataConversionService; + this.clientWritePlatformService = clientWritePlatformService; + } + + // NOT TRANSACTIONAL BY DESIGN FOR NOW + @Override + public EntityIdentifier logCommandSource(final String taskOperation, final String taskEntity, final Long entityId, final String taskJson) { + + final AppUser maker = context.authenticatedUser(); + + final LocalDate asToday = new LocalDate(); + + final CommandSource entity = CommandSource.createdBy(taskOperation, taskEntity, entityId, taskJson, maker, asToday); + + final boolean makerCheckerApproval = false; + Long resourceId = null; + if (entity.isClientResource()) { + final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(entityId, entity.json(), makerCheckerApproval); + if (entity.isCreate()) { + try { + resourceId = this.clientWritePlatformService.createClient(command); + entity.markAsChecked(maker, asToday); + } catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) { + // swallow this rollback transaction by design + } + } else if (entity.isUpdate()) { + try { + EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(command); + resourceId = result.getEntityId(); + entity.markAsChecked(maker, asToday); + } catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) { + // swallow this rollback transaction by design + } + } else if (entity.isDelete()) { + try { + EntityIdentifier result = this.clientWritePlatformService.deleteClient(command); + resourceId = result.getEntityId(); + entity.markAsChecked(maker, asToday); + } catch (RollbackTransactionAsCommandIsNotApprovedByCheckerException e) { + // swallow this rollback transaction by design + } + } + } + + commandSourceRepository.save(entity); + + return EntityIdentifier.makerChecker(resourceId, entity.getId()); + } + + @Transactional + @Override + public EntityIdentifier approveEntry(final Long id) { + + final AppUser checker = context.authenticatedUser(); + + final CommandSource entity = this.commandSourceRepository.findOne(id); + entity.markAsChecked(checker, new LocalDate()); + + Long resourceId = null; + if (entity.isClientResource()) { + final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(entity.resourceId(), entity.json(), true); + if (entity.isCreate()) { + resourceId = this.clientWritePlatformService.createClient(command); + } else if (entity.isUpdate()) { + EntityIdentifier result = this.clientWritePlatformService.updateClientDetails(command); + resourceId = result.getEntityId(); + } else if (entity.isDelete()) { + EntityIdentifier result = this.clientWritePlatformService.deleteClient(command); + resourceId = result.getEntityId(); + } + } + + commandSourceRepository.save(entity); + + return EntityIdentifier.makerChecker(resourceId, entity.getId()); + } + + @Transactional + @Override + public Long deleteEntry(final Long makerCheckerId) { + + context.authenticatedUser(); + + this.commandSourceRepository.delete(makerCheckerId); + + return makerCheckerId; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformService.java new file mode 100644 index 000000000..0abd04a4f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformService.java @@ -0,0 +1,13 @@ +package org.mifosng.platform.makerchecker.service; + +import java.util.Collection; + +import org.mifosng.platform.api.data.CommandSourceData; + +public interface PortfolioCommandsReadPlatformService { + + Collection retrieveAllEntriesToBeChecked(); + + CommandSourceData retrieveById(Long id); + +} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformServiceImpl.java similarity index 51% rename from mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformServiceImpl.java rename to mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformServiceImpl.java index 50a438980..4dab588e7 100644 --- a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioCommandsReadPlatformServiceImpl.java @@ -5,7 +5,7 @@ import java.sql.SQLException; import java.util.Collection; import org.joda.time.LocalDate; -import org.mifosng.platform.api.data.MakerCheckerData; +import org.mifosng.platform.api.data.CommandSourceData; import org.mifosng.platform.infrastructure.JdbcSupport; import org.mifosng.platform.infrastructure.TenantAwareRoutingDataSource; import org.mifosng.platform.security.PlatformSecurityContext; @@ -15,53 +15,56 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; @Service -public class PortfolioMakerCheckerReadPlatformServiceImpl implements PortfolioMakerCheckerReadPlatformService { +public class PortfolioCommandsReadPlatformServiceImpl implements PortfolioCommandsReadPlatformService { private final JdbcTemplate jdbcTemplate; private final PlatformSecurityContext context; @Autowired - public PortfolioMakerCheckerReadPlatformServiceImpl( + public PortfolioCommandsReadPlatformServiceImpl( final PlatformSecurityContext context, final TenantAwareRoutingDataSource dataSource) { this.context = context; this.jdbcTemplate = new JdbcTemplate(dataSource); } - private static final class MakerCheckerMapper implements RowMapper { + private static final class CommandSourceMapper implements RowMapper { public String schema() { - return " mc.id as id, mc.task_name as taskName, mc.task_json as taskJson, mc.made_on_date as madeOnDate from m_maker_checker mc "; + return " mc.id as id, mc.api_operation as taskOperation, mc.api_resource as taskEntity, mc.resource_id as entityId," + + "mc.command_as_json as taskJson, mc.made_on_date as madeOnDate from m_portfolio_command_source mc "; } @Override - public MakerCheckerData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) + public CommandSourceData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { final Long id = rs.getLong("id"); - final String taskName = rs.getString("taskName"); + final String taskOperation = rs.getString("taskOperation"); + final String taskEntity = rs.getString("taskEntity"); + final Long entityId = JdbcSupport.getLong(rs, "entityId"); final String taskJson = rs.getString("taskJson"); final LocalDate madeOnDate = JdbcSupport.getLocalDate(rs, "madeOnDate"); - return new MakerCheckerData(id, taskName, taskJson, madeOnDate); + return new CommandSourceData(id, taskOperation, taskEntity, entityId, taskJson, madeOnDate); } } @Override - public Collection retrieveAllEntriesToBeChecked() { + public Collection retrieveAllEntriesToBeChecked() { context.authenticatedUser(); - final MakerCheckerMapper rm = new MakerCheckerMapper(); - final String sql = "select " + rm.schema() + " order by mc.made_on_date DESC, mc.task_name ASC"; + final CommandSourceMapper rm = new CommandSourceMapper(); + final String sql = "select " + rm.schema() + " where mc.checker_id is null order by mc.made_on_date DESC, mc.api_resource ASC, mc.api_operation ASC"; return this.jdbcTemplate.query(sql, rm, new Object[] {}); } @Override - public MakerCheckerData retrieveById(final Long id) { + public CommandSourceData retrieveById(final Long id) { - final MakerCheckerMapper rm = new MakerCheckerMapper(); + final CommandSourceMapper rm = new CommandSourceMapper(); final String sql = "select " + rm.schema() + " where mc.id=?"; return this.jdbcTemplate.queryForObject(sql, rm, new Object[] {id}); diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformService.java deleted file mode 100644 index 5b3d024bf..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerReadPlatformService.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mifosng.platform.makerchecker.service; - -import java.util.Collection; - -import org.mifosng.platform.api.data.MakerCheckerData; - -public interface PortfolioMakerCheckerReadPlatformService { - - Collection retrieveAllEntriesToBeChecked(); - - MakerCheckerData retrieveById(Long id); - -} diff --git a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerWriteServiceImpl.java b/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerWriteServiceImpl.java deleted file mode 100644 index be9877ff3..000000000 --- a/mifosng-provider/src/main/java/org/mifosng/platform/makerchecker/service/PortfolioMakerCheckerWriteServiceImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.mifosng.platform.makerchecker.service; - -import org.joda.time.LocalDate; -import org.mifosng.platform.api.commands.ClientCommand; -import org.mifosng.platform.api.infrastructure.PortfolioApiDataConversionService; -import org.mifosng.platform.client.service.ClientWritePlatformService; -import org.mifosng.platform.makerchecker.domain.MakerChecker; -import org.mifosng.platform.makerchecker.domain.MakerCheckerRepository; -import org.mifosng.platform.security.PlatformSecurityContext; -import org.mifosng.platform.user.domain.AppUser; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -/** - * - */ -@Service -public class PortfolioMakerCheckerWriteServiceImpl implements PortfolioMakerCheckerWriteService { - - private final PlatformSecurityContext context; - private final MakerCheckerRepository makerCheckerRepository; - private final PortfolioApiDataConversionService apiDataConversionService; - private final ClientWritePlatformService clientWritePlatformService; - - @Autowired - public PortfolioMakerCheckerWriteServiceImpl(final PlatformSecurityContext context, final MakerCheckerRepository makerCheckerRepository, - final PortfolioApiDataConversionService apiDataConversionService, - final ClientWritePlatformService clientWritePlatformService) { - this.context = context; - this.makerCheckerRepository = makerCheckerRepository; - this.apiDataConversionService = apiDataConversionService; - this.clientWritePlatformService = clientWritePlatformService; - } - - @Transactional - @Override - public Long logNewEntry(final String taskName, final String taskJson) { - - final AppUser maker = context.authenticatedUser(); - - final MakerChecker entity = MakerChecker.makerEntry(taskName, taskJson, maker, new LocalDate()); - - makerCheckerRepository.save(entity); - - return entity.getId(); - } - - @Transactional - @Override - public Long approveEntry(final Long id) { - - final AppUser checker = context.authenticatedUser(); - - final MakerChecker entity = this.makerCheckerRepository.findOne(id); - entity.markAsChecked(checker, new LocalDate()); - - makerCheckerRepository.save(entity); - - // assume client creation for now - final ClientCommand command = this.apiDataConversionService.convertJsonToClientCommand(null, entity.json()); - - this.clientWritePlatformService.enrollClient(command); - - return entity.getId(); - } -} \ No newline at end of file diff --git a/mifosng-provider/us.stackdump b/mifosng-provider/us.stackdump deleted file mode 100644 index 70e504d51..000000000 --- a/mifosng-provider/us.stackdump +++ /dev/null @@ -1,16 +0,0 @@ -MSYS-1.0.12 Build:2011-07-20 17:52 -Exception: STATUS_ACCESS_VIOLATION at eip=68085E34 -eax=68560000 ebx=00005D7C ecx=0000175F edx=00000000 esi=00000000 edi=68560000 -ebp=0023FE80 esp=0023FE74 program=us -cs=001B ds=0023 es=0023 fs=003B gs=0000 ss=0023 -Stack trace: -Frame Function Args -0023FE80 68085E34 (68560000, 00000000, 00005D7C, 00000004) -0023FED0 68001CB0 (00020C58, 00000001, 0023FF20, 680044EA) -0023FF20 680045C7 (8600D670, FFDFF538, 0023FF60, 00405F80) -0023FF40 68004C5F (00402318, 00000000, 804FA14C, 0000D670) -0023FF60 68004C98 (00000000, 00000000, A78A3C6C, 00000000) -0023FF90 00405E90 (00402318, 00000000, 00000001, 00000006) -0023FFC0 0040103D (00000000, 00000000, 7FFDE000, 8054A6ED) -0023FFF0 7C816FE7 (00401000, 00000000, 78746341, 00000020) -End of stack trace \ No newline at end of file