diff --git a/mifosng-db/patches/patch-04-add-account-identifier-to-client.sql b/mifosng-db/patches/patch-04-add-account-identifier-to-client.sql new file mode 100644 index 000000000..78c66a203 --- /dev/null +++ b/mifosng-db/patches/patch-04-add-account-identifier-to-client.sql @@ -0,0 +1,12 @@ +ALTER TABLE `m_client` +ADD COLUMN `account_no` VARCHAR(20) NOT NULL AFTER `id`; + +UPDATE `m_client` +SET +`account_no` = `id` +WHERE `id` > 0; + +ALTER TABLE `m_client` ADD UNIQUE INDEX `account_no_UNIQUE` (`account_no` ASC); + + + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/DataTableApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/DataTableApiResource.java index 92f729b69..e24f1a912 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/DataTableApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/api/DataTableApiResource.java @@ -33,210 +33,167 @@ import org.springframework.stereotype.Component; @Scope("singleton") public class DataTableApiResource { - private final PlatformSecurityContext context; - private final GenericDataService genericDataService; - private final ReadWriteNonCoreDataService readWriteNonCoreDataService; - private final ToApiJsonSerializer toApiJsonSerializer; - private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + private final PlatformSecurityContext context; + private final GenericDataService genericDataService; + private final ReadWriteNonCoreDataService readWriteNonCoreDataService; + private final ToApiJsonSerializer toApiJsonSerializer; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; - @Autowired - public DataTableApiResource( - final PlatformSecurityContext context, - final GenericDataService genericDataService, - final ReadWriteNonCoreDataService readWriteNonCoreDataService, - final ToApiJsonSerializer toApiJsonSerializer, - final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { - this.context = context; - this.genericDataService = genericDataService; - this.readWriteNonCoreDataService = readWriteNonCoreDataService; - this.toApiJsonSerializer = toApiJsonSerializer; - this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; - } + @Autowired + public DataTableApiResource(final PlatformSecurityContext context, final GenericDataService genericDataService, + final ReadWriteNonCoreDataService readWriteNonCoreDataService, + final ToApiJsonSerializer toApiJsonSerializer, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) { + this.context = context; + this.genericDataService = genericDataService; + this.readWriteNonCoreDataService = readWriteNonCoreDataService; + this.toApiJsonSerializer = toApiJsonSerializer; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + } - @GET - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String getDatatables(@QueryParam("apptable") final String apptable, - @Context final UriInfo uriInfo) { + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String getDatatables(@QueryParam("apptable") final String apptable, @Context final UriInfo uriInfo) { - final List result = this.readWriteNonCoreDataService - .retrieveDatatableNames(apptable); + final List result = this.readWriteNonCoreDataService.retrieveDatatableNames(apptable); - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo - .getQueryParameters()); - return this.toApiJsonSerializer.serializePretty(prettyPrint, result); - } + final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + return this.toApiJsonSerializer.serializePretty(prettyPrint, result); + } - @POST - @Path("register/{datatable}/{apptable}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String registerDatatable( - @PathParam("datatable") final String datatable, - @PathParam("apptable") final String apptable) { + @POST + @Path("register/{datatable}/{apptable}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String registerDatatable(@PathParam("datatable") final String datatable, @PathParam("apptable") final String apptable) { - this.readWriteNonCoreDataService.registerDatatable(datatable, apptable); + this.readWriteNonCoreDataService.registerDatatable(datatable, apptable); - return this.toApiJsonSerializer.serialize(EntityIdentifier.empty()); - } + return this.toApiJsonSerializer.serialize(EntityIdentifier.empty()); + } - @POST - @Path("deregister/{datatable}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String deregisterDatatable( - @PathParam("datatable") final String datatable) { + @POST + @Path("deregister/{datatable}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deregisterDatatable(@PathParam("datatable") final String datatable) { - this.readWriteNonCoreDataService.deregisterDatatable(datatable); + this.readWriteNonCoreDataService.deregisterDatatable(datatable); - return this.toApiJsonSerializer.serialize(EntityIdentifier.empty()); - } + return this.toApiJsonSerializer.serialize(EntityIdentifier.empty()); + } - @GET - @Path("{datatable}/{apptableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String getDatatable(@PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId, - @QueryParam("order") final String order, - @Context final UriInfo uriInfo) { + @GET + @Path("{datatable}/{apptableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String getDatatable(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId, + @QueryParam("order") final String order, @Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasDatatableReadPermission( - datatable); + context.authenticatedUser().validateHasDatatableReadPermission(datatable); - GenericResultsetData results = this.readWriteNonCoreDataService - .retrieveDataTableGenericResultSet(datatable, apptableId, - order, null); + GenericResultsetData results = this.readWriteNonCoreDataService.retrieveDataTableGenericResultSet(datatable, apptableId, order, + null); - String json = ""; - final boolean genericResultSet = ApiParameterHelper - .genericResultSet(uriInfo.getQueryParameters()); - if (genericResultSet) { - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo - .getQueryParameters()); - json = this.toApiJsonSerializer.serializePretty(prettyPrint, - results); - } else { - json = this.genericDataService - .generateJsonFromGenericResultsetData(results); - } + String json = ""; + final boolean genericResultSet = ApiParameterHelper.genericResultSet(uriInfo.getQueryParameters()); + if (genericResultSet) { + final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + json = this.toApiJsonSerializer.serializePretty(prettyPrint, results); + } else { + json = this.genericDataService.generateJsonFromGenericResultsetData(results); + } - return json; - } + return json; + } - @GET - @Path("{datatable}/{apptableId}/{datatableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String getDatatableManyEntry( - @PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId, - @PathParam("datatableId") final Long datatableId, - @QueryParam("order") final String order, - @Context final UriInfo uriInfo) { + @GET + @Path("{datatable}/{apptableId}/{datatableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String getDatatableManyEntry(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId, + @PathParam("datatableId") final Long datatableId, @QueryParam("order") final String order, @Context final UriInfo uriInfo) { - context.authenticatedUser().validateHasDatatableReadPermission( - datatable); + context.authenticatedUser().validateHasDatatableReadPermission(datatable); - GenericResultsetData results = this.readWriteNonCoreDataService - .retrieveDataTableGenericResultSet(datatable, apptableId, - order, datatableId); + GenericResultsetData results = this.readWriteNonCoreDataService.retrieveDataTableGenericResultSet(datatable, apptableId, order, + datatableId); - String json = ""; - final boolean genericResultSet = ApiParameterHelper - .genericResultSet(uriInfo.getQueryParameters()); - if (genericResultSet) { - final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo - .getQueryParameters()); - json = this.toApiJsonSerializer.serializePretty(prettyPrint, - results); - } else { - json = this.genericDataService - .generateJsonFromGenericResultsetData(results); - } + String json = ""; + final boolean genericResultSet = ApiParameterHelper.genericResultSet(uriInfo.getQueryParameters()); + if (genericResultSet) { + final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters()); + json = this.toApiJsonSerializer.serializePretty(prettyPrint, results); + } else { + json = this.genericDataService.generateJsonFromGenericResultsetData(results); + } - return json; - } + return json; + } - @POST - @Path("{datatable}/{apptableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String newDatatableEntry( - @PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId, - final String apiRequestBodyAsJson) { + @POST + @Path("{datatable}/{apptableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String newDatatableEntry(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId, + final String apiRequestBodyAsJson) { - final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService - .logCommandSource("CREATE", datatable, "CREATE", "datatables/" - + datatable, apptableId, apiRequestBodyAsJson); + final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService.logCommandSource("CREATE", datatable, "CREATE", + "datatables/" + datatable, apptableId, apiRequestBodyAsJson); - return this.toApiJsonSerializer.serialize(entityIdentifier); - } + return this.toApiJsonSerializer.serialize(entityIdentifier); + } - @PUT - @Path("{datatable}/{apptableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String updateDatatableEntryOnetoOne( - @PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId, - final String apiRequestBodyAsJson) { + @PUT + @Path("{datatable}/{apptableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateDatatableEntryOnetoOne(@PathParam("datatable") final String datatable, + @PathParam("apptableId") final Long apptableId, final String apiRequestBodyAsJson) { - final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService - .logCommandSource("UPDATE", datatable, "UPDATE", "datatables/" - + datatable, apptableId, apiRequestBodyAsJson); + final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", datatable, "UPDATE", + "datatables/" + datatable, apptableId, apiRequestBodyAsJson); - return this.toApiJsonSerializer.serialize(entityIdentifier); - } + return this.toApiJsonSerializer.serialize(entityIdentifier); + } - @PUT - @Path("{datatable}/{apptableId}/{datatableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String updateDatatableEntryOneToMany( - @PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId, - @PathParam("datatableId") final Long datatableId, - final String apiRequestBodyAsJson) { + @PUT + @Path("{datatable}/{apptableId}/{datatableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateDatatableEntryOneToMany(@PathParam("datatable") final String datatable, + @PathParam("apptableId") final Long apptableId, @PathParam("datatableId") final Long datatableId, + final String apiRequestBodyAsJson) { - final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService - .logCommandSource("UPDATE", datatable, "UPDATE_MULTIPLE", - "datatables/" + datatable, apptableId, "datatables/" - + datatable, datatableId, apiRequestBodyAsJson); + final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService.logCommandSource("UPDATE", datatable, + "UPDATE_MULTIPLE", "datatables/" + datatable, apptableId, "datatables/" + datatable, datatableId, apiRequestBodyAsJson); - return this.toApiJsonSerializer.serialize(entityIdentifier); - } + return this.toApiJsonSerializer.serialize(entityIdentifier); + } - @DELETE - @Path("{datatable}/{apptableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String deleteDatatableEntries( - @PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId) { + @DELETE + @Path("{datatable}/{apptableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteDatatableEntries(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId) { - final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService - .logCommandSource("DELETE", datatable, "DELETE", "datatables/" - + datatable, apptableId, "{}"); + final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService.logCommandSource("DELETE", datatable, "DELETE", + "datatables/" + datatable, apptableId, "{}"); - return this.toApiJsonSerializer.serialize(entityIdentifier); - } + return this.toApiJsonSerializer.serialize(entityIdentifier); + } - @DELETE - @Path("{datatable}/{apptableId}/{datatableId}") - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - public String deleteDatatableEntries( - @PathParam("datatable") final String datatable, - @PathParam("apptableId") final Long apptableId, - @PathParam("datatableId") final Long datatableId) { + @DELETE + @Path("{datatable}/{apptableId}/{datatableId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteDatatableEntries(@PathParam("datatable") final String datatable, @PathParam("apptableId") final Long apptableId, + @PathParam("datatableId") final Long datatableId) { - final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService - .logCommandSource("DELETE", datatable, "DELETE_MULTIPLE", - "datatables/" + datatable, apptableId, "datatables/" - + datatable, datatableId, "{}"); + final EntityIdentifier entityIdentifier = this.commandsSourceWritePlatformService.logCommandSource("DELETE", datatable, + "DELETE_MULTIPLE", "datatables/" + datatable, apptableId, "datatables/" + datatable, datatableId, "{}"); - return this.toApiJsonSerializer.serialize(entityIdentifier); - } + return this.toApiJsonSerializer.serialize(entityIdentifier); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateOneToOneDatatableEntryCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateOneToOneDatatableEntryCommandHandler.java index 6496746d2..2b06328c6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateOneToOneDatatableEntryCommandHandler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/handler/UpdateOneToOneDatatableEntryCommandHandler.java @@ -1,5 +1,7 @@ package org.mifosplatform.infrastructure.dataqueries.handler; +import java.util.Map; + import org.mifosplatform.commands.handler.NewCommandSourceHandler; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.EntityIdentifier; @@ -9,25 +11,22 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service -public class UpdateOneToOneDatatableEntryCommandHandler implements - NewCommandSourceHandler { +public class UpdateOneToOneDatatableEntryCommandHandler implements NewCommandSourceHandler { - private final ReadWriteNonCoreDataService writePlatformService; + private final ReadWriteNonCoreDataService writePlatformService; - @Autowired - public UpdateOneToOneDatatableEntryCommandHandler( - final ReadWriteNonCoreDataService writePlatformService) { - this.writePlatformService = writePlatformService; - } + @Autowired + public UpdateOneToOneDatatableEntryCommandHandler(final ReadWriteNonCoreDataService writePlatformService) { + this.writePlatformService = writePlatformService; + } - @Transactional - @Override - public EntityIdentifier processCommand(final JsonCommand command) { + @Transactional + @Override + public EntityIdentifier processCommand(final JsonCommand command) { - // TODO - return only detected changes - this.writePlatformService.updateDatatableEntryOneToOne(command.entityName(), command.resourceId(), command); - - return EntityIdentifier.resourceResult(command.resourceId(), - command.commandId()); - } + // TODO - return only detected changes + Map changes = this.writePlatformService.updateDatatableEntryOneToOne(command.entityName(), command.resourceId(), command); + + return EntityIdentifier.resourceResult(command.resourceId(), command.commandId(), changes); + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataService.java index 913286c54..105e24245 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataService.java @@ -1,6 +1,7 @@ package org.mifosplatform.infrastructure.dataqueries.service; import java.util.List; +import java.util.Map; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.dataqueries.data.DatatableData; @@ -9,29 +10,24 @@ import org.springframework.security.access.prepost.PreAuthorize; public interface ReadWriteNonCoreDataService { - List retrieveDatatableNames(String appTable); + List retrieveDatatableNames(String appTable); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'REGISTER_DATATABLE')") - void registerDatatable(String datatable, String appTable); + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'REGISTER_DATATABLE')") + void registerDatatable(String datatable, String appTable); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DEREGISTER_DATATABLE')") - void deregisterDatatable(String datatable); + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DEREGISTER_DATATABLE')") + void deregisterDatatable(String datatable); - GenericResultsetData retrieveDataTableGenericResultSet(String datatable, - Long appTableId, String order, Long id); + GenericResultsetData retrieveDataTableGenericResultSet(String datatable, Long appTableId, String order, Long id); - void newDatatableEntry(String datatable, Long appTableId, - JsonCommand command); + void newDatatableEntry(String datatable, Long appTableId, JsonCommand command); - void updateDatatableEntryOneToOne(String datatable, Long appTableId, - JsonCommand command); + Map updateDatatableEntryOneToOne(String datatable, Long appTableId, JsonCommand command); - void updateDatatableEntryOneToMany(String datatable, Long appTableId, - Long datatableId, JsonCommand command); + void updateDatatableEntryOneToMany(String datatable, Long appTableId, Long datatableId, JsonCommand command); - void deleteDatatableEntries(String datatable, Long appTableId); + void deleteDatatableEntries(String datatable, Long appTableId); - void deleteDatatableEntry(String datatable, Long appTableId, - Long datatableId); + void deleteDatatableEntry(String datatable, Long appTableId, Long datatableId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index fadc4d8d7..2f5ea94b6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -5,6 +5,7 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -37,15 +38,15 @@ import com.google.gson.reflect.TypeToken; @Service public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataService { - private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class); - + private final static Logger logger = LoggerFactory.getLogger(ReadWriteNonCoreDataServiceImpl.class); + private final PlatformSecurityContext context; - private final FromJsonHelper fromJsonHelper; + private final FromJsonHelper fromJsonHelper; @Autowired public ReadWriteNonCoreDataServiceImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper) { this.context = context; - this.fromJsonHelper = fromJsonHelper; + this.fromJsonHelper = fromJsonHelper; } @Autowired @@ -160,7 +161,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ String appTable = getWithinScopeApplicationTableName(datatable, appTableId); List columnHeaders = getDatatableResultsetColumnHeaders(datatable); - + final Type typeOfMap = new TypeToken>() {}.getType(); Map dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json()); @@ -176,9 +177,11 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } @Override - public void updateDatatableEntryOneToOne(final String datatable, final Long appTableId, final JsonCommand command) { + public Map updateDatatableEntryOneToOne(final String datatable, final Long appTableId, final JsonCommand command) { // long startTime = System.currentTimeMillis(); + Map changes = new LinkedHashMap(); + GenericResultsetData grs = retrieveDataTableGenericResultSet(datatable, appTableId, null, null); if (grs.getData().size() == 0) throw new DataTableNotFoundException(datatable, appTableId); @@ -188,10 +191,10 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ + " Foreign Key Id: " + appTableId); String fkName = getFKField(getApplicationTableName(datatable)); - + final Type typeOfMap = new TypeToken>() {}.getType(); Map dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json()); - + String sql = getUpdateSql(grs, datatable, fkName, appTableId, dataParams); if (sql != null) { @@ -204,7 +207,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ // logger.info("FINISHING updateDatatableEntryOnetoOne: Elapsed Time: " // + elapsed + " - datatable: " + datatable + fkName // + ": " + appTableId); - + + return changes; } @Override @@ -218,7 +222,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ final Type typeOfMap = new TypeToken>() {}.getType(); Map dataParams = this.fromJsonHelper.extractDataMap(typeOfMap, command.json()); - + String sql = getUpdateSql(grs, datatable, "id", datatableId, dataParams); if (sql != null) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/data/OfficeData.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/data/OfficeData.java index 993e8dc19..625b8e6aa 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/data/OfficeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/office/data/OfficeData.java @@ -9,74 +9,37 @@ import org.joda.time.LocalDate; */ public class OfficeData { - private final Long id; - private final String name; - private final String nameDecorated; - private final String externalId; - private final LocalDate openingDate; - private final String hierarchy; - private final Long parentId; - private final String parentName; - - private final List allowedParents; - - public static OfficeData template(final List parentLookups, final LocalDate defaultOpeningDate) { - return new OfficeData(null, null, null, null, defaultOpeningDate, null, null, null, parentLookups); - } - - public static OfficeData appendedTemplate(final OfficeData office, final List allowedParents) { - return new OfficeData(office.getId(), office.getName(), office.getNameDecorated(), office.getExternalId(), - office.getOpeningDate(), office.getHierarchy(), office.getParentId(), office.getParentName(), allowedParents); - } + private final Long id; + private final String name; + private final String nameDecorated; + private final String externalId; + private final LocalDate openingDate; + private final String hierarchy; + private final Long parentId; + private final String parentName; - public OfficeData(final Long id, final String name, - final String nameDecorated, final String externalId, - final LocalDate openingDate, String hierarchy, final Long parentId, - final String parentName, final List allowedParents) { - this.id = id; - this.name = name; - this.nameDecorated = nameDecorated; - this.externalId = externalId; - this.openingDate = openingDate; - this.hierarchy = hierarchy; - this.parentName = parentName; - this.parentId = parentId; - this.allowedParents = allowedParents; - } + @SuppressWarnings("unused") + private final List allowedParents; - public Long getId() { - return id; - } + public static OfficeData template(final List parentLookups, final LocalDate defaultOpeningDate) { + return new OfficeData(null, null, null, null, defaultOpeningDate, null, null, null, parentLookups); + } - public String getName() { - return name; - } + public static OfficeData appendedTemplate(final OfficeData office, final List allowedParents) { + return new OfficeData(office.id, office.name, office.nameDecorated, office.externalId, office.openingDate, + office.hierarchy, office.parentId, office.parentName, allowedParents); + } - public String getNameDecorated() { - return nameDecorated; - } - - public String getExternalId() { - return externalId; - } - - public LocalDate getOpeningDate() { - return openingDate; - } - - public String getHierarchy() { - return hierarchy; - } - - public Long getParentId() { - return parentId; - } - - public String getParentName() { - return parentName; - } - - public List getAllowedParents() { - return allowedParents; - } + public OfficeData(final Long id, final String name, final String nameDecorated, final String externalId, final LocalDate openingDate, + String hierarchy, final Long parentId, final String parentName, final List allowedParents) { + this.id = id; + this.name = name; + this.nameDecorated = nameDecorated; + this.externalId = externalId; + this.openingDate = openingDate; + this.hierarchy = hierarchy; + this.parentName = parentName; + this.parentId = parentId; + this.allowedParents = allowedParents; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java index 664596e76..d264edccd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/api/ClientsApiResource.java @@ -86,7 +86,10 @@ public class ClientsApiResource { context.authenticatedUser().validateHasReadPermission("CLIENT"); - final String extraCriteria = getClientCriteria(sqlSearch, officeId, externalId, displayName, firstName, lastName, hierarchy); + // FIXME - kw - rather than exposing SQL through the api can we not just + // pass parameter we want to search by and build up sql like is done for + // other parameters? + final String extraCriteria = buildSqlStringFromClientCriteria(sqlSearch, officeId, externalId, displayName, firstName, lastName, hierarchy); final Collection clients = this.clientReadPlatformService.retrieveAllIndividualClients(extraCriteria); @@ -94,8 +97,8 @@ public class ClientsApiResource { return this.toApiJsonSerializer.serialize(settings, clients, CLIENT_DATA_PARAMETERS); } - private String getClientCriteria(String sqlSearch, Long officeId, String externalId, String displayName, String firstName, - String lastName, String hierarchy) { + private String buildSqlStringFromClientCriteria(final String sqlSearch, final Long officeId, final String externalId, + final String displayName, final String firstName, final String lastName, final String hierarchy) { String extraCriteria = ""; @@ -147,6 +150,7 @@ public class ClientsApiResource { ClientData clientData = this.clientReadPlatformService.retrieveIndividualClient(clientId); if (settings.isTemplate()) { + // FIXME - KW - no need for special OfficeLookup object, just use OfficeData and only populate with id, name, nameDecorated final List allowedOffices = new ArrayList(officeReadPlatformService.retrieveAllOfficesForLookup()); clientData = ClientData.templateOnTop(clientData, allowedOffices); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java index f15b86f57..c52de3113 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/data/ClientData.java @@ -13,6 +13,7 @@ import org.mifosplatform.organisation.office.data.OfficeLookup; final public class ClientData { private final Long id; + private final String accountNo; private final String firstname; private final String lastname; private final String clientOrBusinessName; @@ -55,7 +56,7 @@ final public class ClientData { } final String displayName = buildDisplayNameFrom(firstnameValue, lastnameValue); - return new ClientData(officeId, null, id, firstnameValue, lastnameValue, displayName, externalId, joiningDate, null, null, null, + return new ClientData(null, officeId, null, id, firstnameValue, lastnameValue, displayName, externalId, joiningDate, null, null, null, null); } @@ -67,12 +68,12 @@ final public class ClientData { lastname = clientData.clientOrBusinessName; } final String displayName = buildDisplayNameFrom(firstname, lastname); - return new ClientData(clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName, + return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName, clientData.externalId, clientData.joinedDate, clientData.imageKey, clientData.allowedOffices, currentChange, allChanges); } 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, null, null); + return new ClientData(null, officeId, null, null, null, null, null, null, joinedDate, null, allowedOffices, null, null); } public static ClientData templateOnTop(final ClientData clientData, final List allowedOffices) { @@ -84,22 +85,23 @@ final public class ClientData { lastname = clientData.clientOrBusinessName; } final String displayName = buildDisplayNameFrom(firstname, lastname); - return new ClientData(clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName, + return new ClientData(clientData.accountNo, clientData.officeId, clientData.officeName, clientData.id, firstname, lastname, displayName, clientData.externalId, clientData.joinedDate, clientData.imageKey, allowedOffices, clientData.currentChange, clientData.allChanges); } - public static ClientData clientIdentifier(final Long id, final String firstname, final String lastname, final Long officeId, + public static ClientData clientIdentifier(final Long id, final String accountIdentifier, final String firstname, final String lastname, final Long officeId, final String officeName) { final String displayName = buildDisplayNameFrom(firstname, lastname); - return new ClientData(officeId, officeName, id, firstname, lastname, displayName, null, null, null, null, null, null); + return new ClientData(accountIdentifier, officeId, officeName, id, firstname, lastname, displayName, null, null, null, null, null, null); } - public ClientData(final Long officeId, final String officeName, final Long id, final String firstname, final String lastname, + public ClientData(final String accountNo, 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 ClientData currentChange, final Collection allChanges) { + this.accountNo = accountNo; this.officeId = officeId; this.officeName = officeName; this.id = id; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/AccountIdentifierGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/AccountIdentifierGenerator.java new file mode 100644 index 000000000..2016e8042 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/AccountIdentifierGenerator.java @@ -0,0 +1,12 @@ +package org.mifosplatform.portfolio.client.domain; + +/** + * Responsible for generating unique account identifiers based some rules or patterns. + * + * @see ClientAccountIdentifierGenerator + */ +public interface AccountIdentifierGenerator { + + String generate(); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java index 78223890e..01b9e27c2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/Client.java @@ -15,14 +15,17 @@ import javax.persistence.TemporalType; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; -import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; +import org.mifosplatform.infrastructure.security.service.RandomPasswordGenerator; import org.mifosplatform.organisation.office.domain.Office; -import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.data.jpa.domain.AbstractPersistable; @Entity @Table(name = "m_client") -public class Client extends AbstractAuditableCustom { +public class Client extends AbstractPersistable { + @Column(name = "account_no", length=40, unique=true, nullable=false) + private String accountNumber; + @ManyToOne @JoinColumn(name = "office_id", nullable = false) private Office office; @@ -78,6 +81,7 @@ public class Client extends AbstractAuditableCustom { } private Client(final Office office, final String firstName, final String lastName, final LocalDate openingDate, final String externalId) { + this.accountNumber = new RandomPasswordGenerator(25).generate(); this.office = office; if (StringUtils.isNotBlank(externalId)) { this.externalId = externalId.trim(); @@ -110,6 +114,10 @@ public class Client extends AbstractAuditableCustom { public void changeOffice(final Office newOffice) { this.office = newOffice; } + + public void updateAccountIdentifier(final String accountIdentifier) { + this.accountNumber = accountIdentifier; + } public Map update(final JsonCommand command) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientAccountIdentifierGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientAccountIdentifierGenerator.java new file mode 100644 index 000000000..50a78b0b5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/domain/ClientAccountIdentifierGenerator.java @@ -0,0 +1,21 @@ +package org.mifosplatform.portfolio.client.domain; + +/** + * Example {@link AccountIdentifierGenerator} for clients that takes clients + * auto generated database id and zero fills it ensuring the identifier is + * always of a given maxLength. + */ +public class ClientAccountIdentifierGenerator implements AccountIdentifierGenerator { + + private final Long clientId; + private final int maxLength; + + public ClientAccountIdentifierGenerator(final Long clientId, int maxLength) { + this.clientId = clientId; + this.maxLength = maxLength;} + + @Override + public String generate() { + return String.format("%0" + maxLength + "d", clientId); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java index cc3d1918e..31a7c761b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientReadPlatformServiceImpl.java @@ -130,7 +130,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private static final class ClientMapper implements RowMapper { public String clientSchema() { - return "c.office_id as officeId, o.name as officeName, c.id as id, c.firstname as firstname, c.lastname as lastname, c.display_name as displayName, " + return "c.account_no as accountNo, c.office_id as officeId, o.name as officeName, c.id as id, c.firstname as firstname, c.lastname as lastname, c.display_name as displayName, " + "c.external_id as externalId, c.joined_date as joinedDate, c.image_key as imagekey from m_client c join m_office o on o.id = c.office_id " + " where o.hierarchy like ? and c.is_deleted=0 "; } @@ -138,6 +138,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService @Override public ClientData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final String accountNo = rs.getString("accountNo"); final Long officeId = JdbcSupport.getLong(rs, "officeId"); final Long id = JdbcSupport.getLong(rs, "id"); String firstname = rs.getString("firstname"); @@ -151,7 +152,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final String imageKey = rs.getString("imageKey"); final String officeName = rs.getString("officeName"); - return new ClientData(officeId, officeName, id, firstname, lastname, displayName, externalId, joinedDate, imageKey, null, null, + return new ClientData(accountNo, officeId, officeName, id, firstname, lastname, displayName, externalId, joinedDate, imageKey, null, null, null); } @@ -523,7 +524,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService private static final class ClientIdentifierMapper implements RowMapper { public String clientLookupByIdentifierSchema() { - return "c.id as id, c.firstname as firstname, c.lastname as lastname, " + "c.office_id as officeId, o.name as officeName " + return "c.id as id, c.account_no as accountNo, c.firstname as firstname, c.lastname as lastname, " + "c.office_id as officeId, o.name as officeName " + "from m_client c, m_office o, m_client_identifier ci " + "where o.id = c.office_id and c.id=ci.client_id " + "and ci.document_type_id= ? and ci.document_key like ?"; } @@ -532,6 +533,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService public ClientData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { final Long id = rs.getLong("id"); + final String accountNo = rs.getString("accountNo"); String firstname = rs.getString("firstname"); if (StringUtils.isBlank(firstname)) { firstname = ""; @@ -541,7 +543,7 @@ public class ClientReadPlatformServiceImpl implements ClientReadPlatformService final Long officeId = rs.getLong("officeId"); final String officeName = rs.getString("officeName"); - return ClientData.clientIdentifier(id, firstname, lastname, officeId, officeName); + return ClientData.clientIdentifier(id, accountNo, firstname, lastname, officeId, officeName); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 8ce3f8f87..e11ad7710 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -22,7 +22,9 @@ import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; import org.mifosplatform.portfolio.client.command.ClientCommand; import org.mifosplatform.portfolio.client.command.ClientIdentifierCommand; import org.mifosplatform.portfolio.client.command.ClientNoteCommand; +import org.mifosplatform.portfolio.client.domain.AccountIdentifierGenerator; import org.mifosplatform.portfolio.client.domain.Client; +import org.mifosplatform.portfolio.client.domain.ClientAccountIdentifierGenerator; import org.mifosplatform.portfolio.client.domain.ClientIdentifier; import org.mifosplatform.portfolio.client.domain.ClientIdentifierRepository; import org.mifosplatform.portfolio.client.domain.ClientRepository; @@ -125,7 +127,12 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP final Client newClient = Client.fromJson(clientOffice, command); this.clientRepository.save(newClient); - + + // now use client account identifier generator to generate proper account number + AccountIdentifierGenerator generator = new ClientAccountIdentifierGenerator(newClient.getId(), 9); + newClient.updateAccountIdentifier(generator.generate()); + this.clientRepository.save(newClient); + return EntityIdentifier.resourceResult(newClient.getId(), null); } catch (DataIntegrityViolationException dve) { handleDataIntegrityIssues(command, dve);