You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by GitBox <gi...@apache.org> on 2022/12/02 05:02:21 UTC
[GitHub] [fineract] josehernandezfintecheandomx opened a new pull request, #2783: FINERACT-1760: Client and External Id
josehernandezfintecheandomx opened a new pull request, #2783:
URL: https://github.com/apache/fineract/pull/2783
## Description
The `externalId` of a client is unique, We have added an API to manage clients using the `externalId`
We can to decide whether an externalId is not provided during create a client should it be automatically generated one (UUID) (based on configuration) or leave it empty.
Actions implemented using the externalId
- Client can be fetched
- Client account can be fetched
[FINERACT-1760](https://issues.apache.org/jira/browse/FINERACT-1760)
## Checklist
Please make sure these boxes are checked before submitting your pull request - thanks!
- [ ] Write the commit message as per https://github.com/apache/fineract/#pull-requests
- [ ] Acknowledge that we will not review PRs that are not passing the build _("green")_ - it is your responsibility to get a proposed PR to pass the build, not primarily the project's maintainers.
- [ ] Create/update unit or integration tests for verifying the changes made.
- [ ] Follow coding conventions at https://cwiki.apache.org/confluence/display/FINERACT/Coding+Conventions.
- [ ] Add required Swagger annotation and update API documentation at fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm with details of any API changes
- [ ] Submission is not a "code dump". (Large changes can be made "in repository" via a branch. Ask on the developer mailing list for guidance, if required.)
FYI our guidelines for code reviews are at https://cwiki.apache.org/confluence/display/FINERACT/Code+Review+Guide.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039796013
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -229,7 +232,7 @@ public static Client createNew(final AppUser currentUser, final Office clientOff
final Integer legalForm, final JsonCommand command) {
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
- final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
+ final ExternalId externalId = ExternalId.empty();
Review Comment:
It cannot be empty, you should fetch it from the command and if the configuration is enabled, generate new one. It worth to extract this logic out from the entity. Use a factory / producer to get the data from the command and call the proper constructor.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039990728
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -479,10 +476,10 @@ public Map<String, Object> update(final JsonCommand command) {
this.accountNumber = StringUtils.defaultIfEmpty(newValue, null);
}
- if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName, this.externalId)) {
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName, this.externalId.getValue())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
actualChanges.put(ClientApiConstants.externalIdParamName, newValue);
- this.externalId = StringUtils.defaultIfEmpty(newValue, null);
+ this.externalId = new ExternalId(newValue);
Review Comment:
Done! Passed this value as new parameter
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039270839
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -436,4 +352,251 @@ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientI
final LocalDate transferDate = clientReadPlatformService.retrieveClientTransferProposalDate(clientId);
return toApiJsonSerializer.serialize(transferDate);
}
+
+ @GET
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/accounts")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(apiRequestBodyAsJson) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+
+ final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
+ final CommandWrapper commandRequest = evaluateCommand(clientId, commandParam, builder);
+ CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
Review Comment:
You could extract the similar business logic into a method and that way we dont need to "nearly" duplicate every API logic here.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039618936
##########
gradle.properties:
##########
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
+org.gradle.jvmargs=-Xmx4g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
Review Comment:
For some reasons there is a Java heap issue running the tests, even this was happening in Github
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1047148112
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Please undo the changes you done in the `LoanAccountData` and **only** change the `clientExternalId` as i asked. Please do not change the loan externalid as it was not part of this story and we will conflict.
You have modified the `externalId` field which should have been left AS-IS, it's part of my story to change that and you not changed the `clientExternalId` which should be part of your story. Please undo these latest changes and change only the `clientExternalId` field in the `LoanAccountData`
@josehernandezfintecheandomx is that okay?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039617750
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -290,6 +247,15 @@ public CommandProcessingResult createClient(final JsonCommand command) {
final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProductId, gender,
clientType, clientClassification, legalFormValue, command);
+
+ ExternalId externalId = null;
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040850809
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ private String applyCommandOverClient(Long clientId, String externalId, final String command, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonPayload);
+ final CommandWrapper commandRequest = evaluateCommand(clientId, command, builder);
+ CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientObligeeDetails(Long clientId, String externalId) {
Review Comment:
externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040847505
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
Review Comment:
externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040895441
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testUpdateClientUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = ClientHelper.getBasicClientAsJSON(null, ClientHelper.LEGALFORM_ID_PERSON, clientExternalId);
+ final PutClientsClientIdResponse clientUpdateResponse = ClientHelper.updateClient(requestSpec, responseSpec, clientExternalId,
+ jsonPayload);
+ // then
+ assertNotNull(clientUpdateResponse);
+ assertNotNull(clientUpdateResponse.getResourceExternalId());
+ assertEquals(clientExternalId, clientUpdateResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testDeleteClientUsingExternalId() {
+ // given
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+
+ // then
+ final DeleteClientsClientIdResponse clientDeleteResponse = ClientHelper.deleteClient(requestSpec, responseSpec, clientExternalId);
+ assertNotNull(clientDeleteResponse);
+ assertNotNull(clientDeleteResponse.getResourceExternalId());
+ assertEquals(clientExternalId, clientDeleteResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testGetClientAccountsUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+
+ GetClientsClientIdAccountsResponse clientAccountsResponse = ClientHelper.getClientAccounts(requestSpec, responseSpec,
+ clientExternalId);
+
+ // then
+ assertNotNull(clientAccountsResponse);
Review Comment:
Please check whether it retrieves the proper client (id and externalId)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039793683
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -379,7 +383,7 @@ public static ClientData clientIdentifier(final Long id, final String accountNo,
final Long transferToOfficeId = null;
final String transferToOfficeName = null;
- final String externalId = null;
+ final ExternalId externalId = null;
Review Comment:
ExternalId type object cannot be allowed to be null! Use `ExternalId.empty()`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039292804
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -290,6 +247,15 @@ public CommandProcessingResult createClient(final JsonCommand command) {
final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProductId, gender,
clientType, clientClassification, legalFormValue, command);
+
+ ExternalId externalId = null;
Review Comment:
Use the ExternalIdFactory please!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039786406
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/client/ClientEntityImportHandler.java:
##########
@@ -141,7 +143,7 @@ private ClientData readClient(final Workbook workbook, final Row row, final Stri
ClientNonPersonData clientNonPersonData = ClientNonPersonData.importInstance(incorporationNo, incorporationTill, remarks,
mainBusinessId, constitutionId, locale, dateFormat);
- String externalId = ImportHandlerUtils.readAsString(ClientEntityConstants.EXTERNAL_ID_COL, row);
+ ExternalId externalId = ExternalIdFactory.produce(ImportHandlerUtils.readAsString(ClientEntityConstants.EXTERNAL_ID_COL, row));
Review Comment:
Would it make more sense to use ExternalIdFactory.create()? it would auto generate one if the configuration is enabled and value was not provided
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] galovics commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
galovics commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1042072148
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
- sqlBuilder.append(
+ builder.append(
"c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, ");
- sqlBuilder.append(
+ builder.append(
"cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, ");
- sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
- sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
- sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
- sqlBuilder.append("c.mobile_no as mobileNo, ");
- sqlBuilder.append("c.is_staff as isStaff, ");
- sqlBuilder.append("c.email_address as emailAddress, ");
- sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
- sqlBuilder.append("c.gender_cv_id as genderId, ");
- sqlBuilder.append("cv.code_value as genderValue, ");
- sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
- sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
- sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
- sqlBuilder.append("cvclassification.code_value as classificationValue, ");
- sqlBuilder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
- sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
- sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
- sqlBuilder.append("c.default_savings_account as savingsAccountId, ");
-
- sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
- sqlBuilder.append("sbu.username as submittedByUsername, ");
- sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
- sqlBuilder.append("sbu.lastname as submittedByLastname, ");
+ builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
+ builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
+ builder.append("c.fullname as fullname, c.display_name as displayName, ");
+ builder.append("c.mobile_no as mobileNo, ");
+ builder.append("c.is_staff as isStaff, ");
+ builder.append("c.email_address as emailAddress, ");
+ builder.append("c.date_of_birth as dateOfBirth, ");
+ builder.append("c.gender_cv_id as genderId, ");
+ builder.append("cv.code_value as genderValue, ");
+ builder.append("c.client_type_cv_id as clienttypeId, ");
+ builder.append("cvclienttype.code_value as clienttypeValue, ");
+ builder.append("c.client_classification_cv_id as classificationId, ");
+ builder.append("cvclassification.code_value as classificationValue, ");
+ builder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.closedon_date as closedOnDate, ");
- sqlBuilder.append("clu.username as closedByUsername, ");
- sqlBuilder.append("clu.firstname as closedByFirstname, ");
- sqlBuilder.append("clu.lastname as closedByLastname, ");
+ builder.append("c.submittedon_date as submittedOnDate, ");
+ builder.append("sbu.username as submittedByUsername, ");
+ builder.append("sbu.firstname as submittedByFirstname, ");
+ builder.append("sbu.lastname as submittedByLastname, ");
- sqlBuilder.append("acu.username as activatedByUsername, ");
- sqlBuilder.append("acu.firstname as activatedByFirstname, ");
- sqlBuilder.append("acu.lastname as activatedByLastname, ");
+ builder.append("c.closedon_date as closedOnDate, ");
+ builder.append("clu.username as closedByUsername, ");
+ builder.append("clu.firstname as closedByFirstname, ");
+ builder.append("clu.lastname as closedByLastname, ");
- sqlBuilder.append("cnp.constitution_cv_id as constitutionId, ");
- sqlBuilder.append("cvConstitution.code_value as constitutionValue, ");
- sqlBuilder.append("cnp.incorp_no as incorpNo, ");
- sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
- sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
- sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
- sqlBuilder.append("cnp.remarks as remarks ");
+ // builder.append("c.submittedon as submittedOnDate, ");
Review Comment:
I don't know why this was left here as a comment. Also as I wrote above, please move back everything to its original place.
##########
gradle.properties:
##########
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
+org.gradle.jvmargs=-Xmx4g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
Review Comment:
Please revert this.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
- sqlBuilder.append(
+ builder.append(
"c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, ");
- sqlBuilder.append(
+ builder.append(
"cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, ");
- sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
- sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
- sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
- sqlBuilder.append("c.mobile_no as mobileNo, ");
- sqlBuilder.append("c.is_staff as isStaff, ");
- sqlBuilder.append("c.email_address as emailAddress, ");
- sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
- sqlBuilder.append("c.gender_cv_id as genderId, ");
- sqlBuilder.append("cv.code_value as genderValue, ");
- sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
- sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
- sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
- sqlBuilder.append("cvclassification.code_value as classificationValue, ");
- sqlBuilder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
- sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
- sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
- sqlBuilder.append("c.default_savings_account as savingsAccountId, ");
-
- sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
- sqlBuilder.append("sbu.username as submittedByUsername, ");
- sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
- sqlBuilder.append("sbu.lastname as submittedByLastname, ");
+ builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
+ builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
+ builder.append("c.fullname as fullname, c.display_name as displayName, ");
+ builder.append("c.mobile_no as mobileNo, ");
+ builder.append("c.is_staff as isStaff, ");
+ builder.append("c.email_address as emailAddress, ");
+ builder.append("c.date_of_birth as dateOfBirth, ");
+ builder.append("c.gender_cv_id as genderId, ");
+ builder.append("cv.code_value as genderValue, ");
+ builder.append("c.client_type_cv_id as clienttypeId, ");
+ builder.append("cvclienttype.code_value as clienttypeValue, ");
+ builder.append("c.client_classification_cv_id as classificationId, ");
+ builder.append("cvclassification.code_value as classificationValue, ");
+ builder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.closedon_date as closedOnDate, ");
- sqlBuilder.append("clu.username as closedByUsername, ");
- sqlBuilder.append("clu.firstname as closedByFirstname, ");
- sqlBuilder.append("clu.lastname as closedByLastname, ");
+ builder.append("c.submittedon_date as submittedOnDate, ");
+ builder.append("sbu.username as submittedByUsername, ");
+ builder.append("sbu.firstname as submittedByFirstname, ");
+ builder.append("sbu.lastname as submittedByLastname, ");
- sqlBuilder.append("acu.username as activatedByUsername, ");
- sqlBuilder.append("acu.firstname as activatedByFirstname, ");
- sqlBuilder.append("acu.lastname as activatedByLastname, ");
+ builder.append("c.closedon_date as closedOnDate, ");
+ builder.append("clu.username as closedByUsername, ");
+ builder.append("clu.firstname as closedByFirstname, ");
+ builder.append("clu.lastname as closedByLastname, ");
- sqlBuilder.append("cnp.constitution_cv_id as constitutionId, ");
- sqlBuilder.append("cvConstitution.code_value as constitutionValue, ");
- sqlBuilder.append("cnp.incorp_no as incorpNo, ");
- sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
- sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
- sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
- sqlBuilder.append("cnp.remarks as remarks ");
+ // builder.append("c.submittedon as submittedOnDate, ");
+ builder.append("acu.username as activatedByUsername, ");
+ builder.append("acu.firstname as activatedByFirstname, ");
+ builder.append("acu.lastname as activatedByLastname, ");
- sqlBuilder.append("from m_client c ");
- sqlBuilder.append("join m_office o on o.id = c.office_id ");
- sqlBuilder.append("left join m_client_non_person cnp on cnp.client_id = c.id ");
- sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id ");
Review Comment:
This has been deleted. I know it wasn't used in the select but this is an inner join which implicitly does filtering on the dataset. Move it back pls. Also as I wrote above, please move back everything to its original place.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039275106
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResourceSwagger.java:
##########
@@ -252,6 +252,8 @@ private GetClientsGroups() {}
public List<GetClientsGroups> groups;
@Schema(example = "test@test.com")
public String emailAddress;
+ @Schema(example = "123")
+ public String externalId;
Review Comment:
I think more than 2 API should retrieve the external id as well. It should be retrieved in all cases (where it makes sense). Like: GetClientsResponse, PutClientsClientIdResponse, ...
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1046085648
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
Review Comment:
The variable never was renamed, Please take a look of the next screenshot to you can see the git diff is getting that difference...
<img width="1034" alt="Screenshot 2022-12-12 at 10 46 22" src="https://user-images.githubusercontent.com/44206706/207104028-b577942c-e156-4483-886a-8aa219246d57.png">
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1047148112
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Please undo the changes you done in the `LoanAccountData` and **only** change the `clientExternalId` as i asked. Please do not change the loan externalid as it was not part of this story and we will conflict.
You have modified the `externalId` field which should have been left AS-IS, it's part of my story to change that and you havent changed the `clientExternalId` which was part of your story. Please undo these latest changes and change only the `clientExternalId` field in the `LoanAccountData`
@josehernandezfintecheandomx is that okay?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041068946
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ private String applyCommandOverClient(Long clientId, String externalId, final String command, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
Review Comment:
Done
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ private String applyCommandOverClient(Long clientId, String externalId, final String command, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonPayload);
+ final CommandWrapper commandRequest = evaluateCommand(clientId, command, builder);
+ CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientObligeeDetails(Long clientId, String externalId) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041068105
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
Review Comment:
Done
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041073352
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -763,30 +750,18 @@ public boolean isStaff() {
return this.isStaff;
}
- public String getExternalId() {
- return this.externalId;
+ public void setExternalId(ExternalId externalId) {
Review Comment:
Removed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040886420
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -463,7 +459,7 @@ public boolean isWithdrawn() {
return ClientStatus.fromInt(this.status).isWithdrawn();
}
- public Map<String, Object> update(final JsonCommand command) {
+ public Map<String, Object> update(final ExternalId externalId, final JsonCommand command) {
Review Comment:
Please move all the "command" related logic out of the entity.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039887260
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/client/ClientPersonImportHandler.java:
##########
@@ -95,7 +97,7 @@ private ClientData readClient(final Workbook workbook, final Row row, final Stri
if (staffId == 0L) {
staffId = null;
}
- String externalId = ImportHandlerUtils.readAsString(ClientPersonConstants.EXTERNAL_ID_COL, row);
+ ExternalId externalId = ExternalIdFactory.produce(ImportHandlerUtils.readAsString(ClientPersonConstants.EXTERNAL_ID_COL, row));
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040265519
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -24,92 +24,96 @@
import java.util.List;
import java.util.Set;
import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.address.data.AddressData;
-import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
/**
* Immutable data object representing client data.
*/
+@NoArgsConstructor
@Getter
+@Setter
public final class ClientData implements Comparable<ClientData>, Serializable {
- private final Long id;
- private final String accountNo;
- private final String externalId;
-
- private final EnumOptionData status;
- private final CodeValueData subStatus;
-
- private final Boolean active;
- private final LocalDate activationDate;
-
- private final String firstname;
- private final String middlename;
- private final String lastname;
- private final String fullname;
- private final String displayName;
- private final String mobileNo;
- private final String emailAddress;
- private final LocalDate dateOfBirth;
- private final CodeValueData gender;
- private final CodeValueData clientType;
- private final CodeValueData clientClassification;
- private final Boolean isStaff;
-
- private final Long officeId;
- private final String officeName;
- private final Long transferToOfficeId;
- private final String transferToOfficeName;
-
- private final Long imageId;
- private final Boolean imagePresent;
- private final Long staffId;
- private final String staffName;
- private final ClientTimelineData timeline;
-
- private final Long savingsProductId;
- private final String savingsProductName;
-
- private final Long savingsAccountId;
- private final EnumOptionData legalForm;
- private final Set<ClientCollateralManagementData> clientCollateralManagements;
+ private Long id;
+ private String accountNo;
+ private ExternalId externalId;
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039606278
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientRepository.java:
##########
@@ -29,4 +29,35 @@ interface ClientRepository extends JpaRepository<Client, Long>, JpaSpecification
@Query(FIND_CLIENT_BY_ACCOUNT_NUMBER)
Client getClientByAccountNumber(@Param("accountNumber") String accountNumber);
+
+ @Query("""
+ SELECT client.id
+ FROM Client client
+ WHERE client.externalId = :externalId
+ """)
+ Long getClientIdByExternalId(String externalId);
+
+ @Query("""
+ SELECT client
+ FROM Client client
+ JOIN client.office office
+ LEFT JOIN client.transferToOffice transferToOffice
+ WHERE client.id = :clientId
+ AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
+ """)
+ Client fetchByClientIdAndHierarchy(Long clientId, String officeHierarchy, String transferToOfficeHierarchy);
+
+ @Query("""
+ SELECT client
+ FROM Client client
+ JOIN client.office office
+ LEFT JOIN client.transferToOffice transferToOffice
+ WHERE client.externalId = :externalId
+ AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
+ """)
+ Client fetchByExternalIdAndHierarchy(String externalId, String officeHierarchy, String transferToOfficeHierarchy);
+
+ @Query("SELECT c.id FROM Client c WHERE c.externalId = :externalId")
+ Long findIdByExternalId(@Param("externalId") String externalId);
Review Comment:
`getClientIdByExternalId` was removed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039989747
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -229,7 +232,7 @@ public static Client createNew(final AppUser currentUser, final Office clientOff
final Integer legalForm, final JsonCommand command) {
final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
- final String externalId = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
+ final ExternalId externalId = ExternalId.empty();
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039802744
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -290,6 +247,16 @@ public CommandProcessingResult createClient(final JsonCommand command) {
final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProductId, gender,
clientType, clientClassification, legalFormValue, command);
+
+ final String externalIdValue = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
Review Comment:
Use `ExternalIdFactory`. you dont need to to manually these checks!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041379158
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -463,7 +459,7 @@ public boolean isWithdrawn() {
return ClientStatus.fromInt(this.status).isWithdrawn();
}
- public Map<String, Object> update(final JsonCommand command) {
+ public Map<String, Object> update(final ExternalId externalId, final JsonCommand command) {
Review Comment:
i thought you will move this logic into the only service which calls it... :/
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040847320
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
Review Comment:
Params can be final
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
Review Comment:
clientId and externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041069351
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ private String applyCommandOverClient(Long clientId, String externalId, final String command, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonPayload);
+ final CommandWrapper commandRequest = evaluateCommand(clientId, command, builder);
+ CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientObligeeDetails(Long clientId, String externalId) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ return toApiJsonSerializer.serialize(ObligeeList);
+ }
+
+ private String retrieveClientTransferTemplate(Long clientId, String externalId) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041064743
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041317869
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -224,12 +227,11 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom {
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<ClientCollateralManagement> clientCollateralManagements = new HashSet<>();
- public static Client createNew(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff,
+ public static Client instance(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff,
final Long savingsProductId, final CodeValue gender, final CodeValue clientType, final CodeValue clientClassification,
- final Integer legalForm, final JsonCommand command) {
+ final Integer legalForm, final ExternalId externalId, final JsonCommand command) {
Review Comment:
Done, Interface and Service created `ClientDomainService`
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -463,7 +459,7 @@ public boolean isWithdrawn() {
return ClientStatus.fromInt(this.status).isWithdrawn();
}
- public Map<String, Object> update(final JsonCommand command) {
+ public Map<String, Object> update(final ExternalId externalId, final JsonCommand command) {
Review Comment:
Done, Interface and Service created `ClientDomainService`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044464638
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java:
##########
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.mapper;
+
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.client.data.ClientData;
+import org.apache.fineract.portfolio.client.data.ClientTimelineData;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+@Mapper(config = MapstructMapperConfig.class)
+public interface ClientMapper {
+
+ @Mapping(target = "accountNo", source = "source.accountNumber")
+ @Mapping(target = "status", source = "source", qualifiedByName = "clientStatusEnum")
+ @Mapping(target = "subStatus", source = "source", qualifiedByName = "clientSubStatusCode")
+ @Mapping(target = "officeId", source = "source.office.id")
+ @Mapping(target = "officeName", source = "source.office.name")
+ @Mapping(target = "transferToOfficeId", source = "source.transferToOffice.id")
+ @Mapping(target = "transferToOfficeName", source = "source.transferToOffice.name")
+ @Mapping(target = "externalId", source = "source.externalId")
+ @Mapping(target = "gender", source = "source", qualifiedByName = "clientGenderCode")
+ @Mapping(target = "imageId", source = "source.image.id")
+ @Mapping(target = "staffId", source = "source.staff.id")
+ @Mapping(target = "staffName", source = "source.staff.displayName")
+ @Mapping(target = "timeline", source = "source", qualifiedByName = "clientTimeloneData")
+ @Mapping(target = "savingsProductId", source = "source.savingsProductId")
+ @Mapping(target = "savingsProductName", source = "source.id")
+ @Mapping(target = "savingsAccountId", source = "source.savingsAccountId")
+ @Mapping(target = "clientType", source = "source", qualifiedByName = "clientTypeCode")
+ @Mapping(target = "clientClassification", source = "source", qualifiedByName = "clientClassificationCode")
+ @Mapping(target = "legalForm", source = "source", qualifiedByName = "clientLegalFormEnum")
+ @Mapping(target = "isStaff", source = "source", qualifiedByName = "clientIsStaff")
+ @Mapping(target = "imagePresent", ignore = true)
Review Comment:
The `ClientData` dto is used for different scenarios, Individual clients, Group clients for instance. So In this specific case we are talking just for Individual clients and the other attributes are not filled/used in this case and that is the way to be not filled (ignoring them)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039287147
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientRepository.java:
##########
@@ -29,4 +29,35 @@ interface ClientRepository extends JpaRepository<Client, Long>, JpaSpecification
@Query(FIND_CLIENT_BY_ACCOUNT_NUMBER)
Client getClientByAccountNumber(@Param("accountNumber") String accountNumber);
+
+ @Query("""
+ SELECT client.id
+ FROM Client client
+ WHERE client.externalId = :externalId
+ """)
+ Long getClientIdByExternalId(String externalId);
+
+ @Query("""
+ SELECT client
+ FROM Client client
+ JOIN client.office office
+ LEFT JOIN client.transferToOffice transferToOffice
+ WHERE client.id = :clientId
+ AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
+ """)
+ Client fetchByClientIdAndHierarchy(Long clientId, String officeHierarchy, String transferToOfficeHierarchy);
+
+ @Query("""
Review Comment:
We dont need this. We should not fetch any data by the externalid, rather fetch the client id by the external id and leave untouched the rest of the logic to avoid duplications
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039704997
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -34,82 +36,83 @@
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.address.data.AddressData;
-import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
/**
* Immutable data object representing client data.
*/
+@NoArgsConstructor
@Getter
+@Setter
public final class ClientData implements Comparable<ClientData>, Serializable {
- private final Long id;
- private final String accountNo;
- private final String externalId;
-
- private final EnumOptionData status;
- private final CodeValueData subStatus;
-
- private final Boolean active;
- private final LocalDate activationDate;
-
- private final String firstname;
- private final String middlename;
- private final String lastname;
- private final String fullname;
- private final String displayName;
- private final String mobileNo;
- private final String emailAddress;
- private final LocalDate dateOfBirth;
- private final CodeValueData gender;
- private final CodeValueData clientType;
- private final CodeValueData clientClassification;
- private final Boolean isStaff;
-
- private final Long officeId;
- private final String officeName;
- private final Long transferToOfficeId;
- private final String transferToOfficeName;
-
- private final Long imageId;
- private final Boolean imagePresent;
- private final Long staffId;
- private final String staffName;
- private final ClientTimelineData timeline;
-
- private final Long savingsProductId;
- private final String savingsProductName;
-
- private final Long savingsAccountId;
- private final EnumOptionData legalForm;
- private final Set<ClientCollateralManagementData> clientCollateralManagements;
+ private Long id;
+ private String accountNo;
+ private String externalId;
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039786406
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/client/ClientEntityImportHandler.java:
##########
@@ -141,7 +143,7 @@ private ClientData readClient(final Workbook workbook, final Row row, final Stri
ClientNonPersonData clientNonPersonData = ClientNonPersonData.importInstance(incorporationNo, incorporationTill, remarks,
mainBusinessId, constitutionId, locale, dateFormat);
- String externalId = ImportHandlerUtils.readAsString(ClientEntityConstants.EXTERNAL_ID_COL, row);
+ ExternalId externalId = ExternalIdFactory.produce(ImportHandlerUtils.readAsString(ClientEntityConstants.EXTERNAL_ID_COL, row));
Review Comment:
Would it make more sense to use `ExternalIdFactory.create()`? it would auto generate one if the configuration is enabled and value was not provided
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039800321
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -479,10 +476,10 @@ public Map<String, Object> update(final JsonCommand command) {
this.accountNumber = StringUtils.defaultIfEmpty(newValue, null);
}
- if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName, this.externalId)) {
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName, this.externalId.getValue())) {
final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
actualChanges.put(ClientApiConstants.externalIdParamName, newValue);
- this.externalId = StringUtils.defaultIfEmpty(newValue, null);
+ this.externalId = new ExternalId(newValue);
Review Comment:
This is wrong! Please move this logic out of the entity.
Also, creating a new external id please use the `ExternalIdFactory`.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040264438
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ private void fetchClientByExternalId(final String externalId) {
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, externalId);
+ assertNotNull(clientResponse);
+ assertEquals(externalId, clientResponse.getExternalId());
+ }
+}
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1049303799
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java:
##########
@@ -165,7 +153,7 @@ private String generateAccountNumber(Map<String, String> propertyMap, AccountNum
prefix = prefix.substring(0, Math.min(prefix.length(), 10));
}
if (accountNumberPrefixType.getValue().equals(AccountNumberPrefixType.PREFIX_SHORT_NAME.getValue())) {
- Integer prefixLength = prefix.length();
+ Integer prefixLength = prefix == null ? 10 : prefix.length();
Review Comment:
This is a Potential null pointer access, but If that makes you happy, It has been reverted
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044478616
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -545,90 +543,77 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
- @Override
- public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
-
- final AppUser currentUser = this.context.authenticatedUser();
- final String hierarchy = currentUser.getOffice().getHierarchy();
- final String hierarchySearchString = hierarchy + "%";
-
- final String sql = "select " + this.membersOfGroupMapper.schema()
- + " left join m_group g on pgc.group_id=g.id where o.hierarchy like ? and g.parent_id = ? and c.status_enum = ? group by c.id";
-
- return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, // NOSONAR
- hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue());
- }
-
- private static final class ClientMapper implements RowMapper<ClientData> {
+ private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMapper() {
- final StringBuilder builder = new StringBuilder(400);
+ ClientMembersOfGroupMapper() {
+ final StringBuilder sqlBuilder = new StringBuilder(200);
Review Comment:
There are different (old) Client mapper for covering different flows, individual client, group client, etc. In this case It was not renamed, we remove the ClientMapper to be now as MapStruct mapper
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044479707
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
Review Comment:
There are different (old) Client mapper for covering different flows, individual client, group client, etc. In this case It was not renamed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040851100
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ private String applyCommandOverClient(Long clientId, String externalId, final String command, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(jsonPayload);
+ final CommandWrapper commandRequest = evaluateCommand(clientId, command, builder);
+ CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientObligeeDetails(Long clientId, String externalId) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ return toApiJsonSerializer.serialize(ObligeeList);
+ }
+
+ private String retrieveClientTransferTemplate(Long clientId, String externalId) {
Review Comment:
externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040845861
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
Review Comment:
This can be private
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040894541
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
You know the value of the external id, so please do equality check instead of null check here
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
You know the value of the external id, so please do equality check instead of null check here
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041079865
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
Done
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1047148112
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Please undo the changes you done in the `LoanAccountData` and only change the `clientExternalId` as i asked. Please do not change the loan externalid as it was not part of this story and we will conflict.
@josehernandezfintecheandomx is that okay?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1049300800
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -559,11 +559,143 @@ public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long cen
hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMapper implements RowMapper<ClientData> {
+ private static final class ParentGroupsMapper implements RowMapper<GroupGeneralData> {
Review Comment:
Pretty simple, It was not reordered, ClientMapper was replaced with a mapper using MapStruct
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1047635664
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039264757
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java:
##########
@@ -91,6 +84,29 @@ public AccountSummaryCollectionData retrieveClientAccountDetails(final Long clie
return new AccountSummaryCollectionData(loanAccounts, glimAccounts, savingsAccounts, shareAccounts, guarantorloanAccounts);
}
+ @Override
+ public AccountSummaryCollectionData retrieveClientAccountDetails(String externalId) {
Review Comment:
We discussed we dont need this. You can resolve the internal id at the API layer, the same way as it was done for the Loan transactions. This way we dont need to introduce new methods and business logic.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] galovics commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
galovics commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039521823
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java:
##########
@@ -0,0 +1,169 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.mapper;
+
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.client.data.ClientData;
+import org.apache.fineract.portfolio.client.data.ClientTimelineData;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+@Mapper(config = MapstructMapperConfig.class)
+public interface ClientMapper {
+
+ @Mapping(target = "id", source = "source.id")
Review Comment:
Default mapping logic wil ltake care of this. That way 99% of these annotations are unnecessary.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -763,30 +757,18 @@ public boolean isStaff() {
return this.isStaff;
}
- public String getExternalId() {
- return this.externalId;
+ public void setExternalId(String externalId) {
Review Comment:
And not only the method parameter but the attribute type as well.
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
Review Comment:
We already had a Client related test class, didn't we? Why a new one?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039880418
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -436,4 +352,251 @@ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientI
final LocalDate transferDate = clientReadPlatformService.retrieveClientTransferProposalDate(clientId);
return toApiJsonSerializer.serialize(transferDate);
}
+
+ @GET
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/accounts")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(apiRequestBodyAsJson) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+
+ final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
+ final CommandWrapper commandRequest = evaluateCommand(clientId, commandParam, builder);
+ CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ final Long clientId = getResolvedClientId(null, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039881056
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java:
##########
@@ -0,0 +1,169 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.mapper;
+
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.client.data.ClientData;
+import org.apache.fineract.portfolio.client.data.ClientTimelineData;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+@Mapper(config = MapstructMapperConfig.class)
+public interface ClientMapper {
+
+ @Mapping(target = "id", source = "source.id")
Review Comment:
Removed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039890825
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -379,7 +383,7 @@ public static ClientData clientIdentifier(final Long id, final String accountNo,
final Long transferToOfficeId = null;
final String transferToOfficeName = null;
- final String externalId = null;
+ final ExternalId externalId = null;
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041911174
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientDomainServiceImpl.java:
##########
@@ -0,0 +1,225 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.domain;
+
+import java.time.LocalDate;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.office.domain.Office;
+import org.apache.fineract.organisation.staff.domain.Staff;
+import org.apache.fineract.portfolio.client.api.ClientApiConstants;
+import org.apache.fineract.portfolio.group.domain.Group;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ClientDomainServiceImpl implements ClientDomainService {
+
+ @Override
+ public Client createClient(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff,
+ final Long savingsProductId, final CodeValue gender, final CodeValue clientType, final CodeValue clientClassification,
+ final Integer legalForm, final ExternalId externalId, final JsonCommand command) {
+
+ final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
+ final String mobileNo = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
+ final String emailAddress = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
+
+ final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
+ final String middlename = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
+ final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
+ final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
+
+ final boolean isStaff = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.isStaffParamName);
+
+ final LocalDate dataOfBirth = command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName);
+
+ ClientStatus status = ClientStatus.PENDING;
+ boolean active = false;
+ if (command.hasParameter("active")) {
+ active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName);
+ }
+
+ LocalDate activationDate = null;
+ LocalDate officeJoiningDate = null;
+ if (active) {
+ status = ClientStatus.ACTIVE;
+ activationDate = command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName);
+ officeJoiningDate = activationDate;
+ }
+
+ LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
+ if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) {
+ submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
+ }
+ if (active && submittedOnDate.isAfter(activationDate)) {
+ submittedOnDate = activationDate;
+ }
+ final Long savingsAccountId = null;
+
+ return Client.instance(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname,
+ activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff, submittedOnDate, savingsProductId,
+ savingsAccountId, dataOfBirth, gender, clientType, clientClassification, legalForm, isStaff);
+
+ }
+
+ @Override
+ public Map<String, Object> updateClient(Client client, final ExternalId externalId, final JsonCommand command) {
+
+ final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
+
+ if (command.isChangeInIntegerParameterNamed(ClientApiConstants.statusParamName, client.getStatus())) {
+ final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.statusParamName);
+ actualChanges.put(ClientApiConstants.statusParamName, ClientEnumerations.status(newValue));
+ client.setStatus(ClientStatus.fromInt(newValue).getValue());
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.accountNoParamName, client.getAccountNumber())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
+ actualChanges.put(ClientApiConstants.accountNoParamName, newValue);
+ client.setAccountNumber(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName, externalId.getValue())) {
+ actualChanges.put(ClientApiConstants.externalIdParamName, externalId.getValue());
+ client.setExternalId(externalId);
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.mobileNoParamName, client.getMobileNo())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
+ actualChanges.put(ClientApiConstants.mobileNoParamName, newValue);
+ client.setMobileNo(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.emailAddressParamName, client.getEmailAddress())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
+ actualChanges.put(ClientApiConstants.emailAddressParamName, newValue);
+ client.setEmailAddress(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.firstnameParamName, client.getFirstname())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
+ actualChanges.put(ClientApiConstants.firstnameParamName, newValue);
+ client.setFirstname(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.middlenameParamName, client.getMiddlename())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
+ actualChanges.put(ClientApiConstants.middlenameParamName, newValue);
+ client.setMiddlename(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.lastnameParamName, client.getLastname())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
+ actualChanges.put(ClientApiConstants.lastnameParamName, newValue);
+ client.setLastname(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.fullnameParamName, client.getFullname())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
+ actualChanges.put(ClientApiConstants.fullnameParamName, newValue);
+ client.setFullname(newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.staffIdParamName, client.staffId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
+ actualChanges.put(ClientApiConstants.staffIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.genderIdParamName, client.genderId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
+ actualChanges.put(ClientApiConstants.genderIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.savingsProductIdParamName, client.savingsProductId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
+ actualChanges.put(ClientApiConstants.savingsProductIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.clientTypeIdParamName, client.clientTypeId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
+ actualChanges.put(ClientApiConstants.clientTypeIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.clientClassificationIdParamName, client.clientClassificationId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientClassificationIdParamName);
+ actualChanges.put(ClientApiConstants.clientClassificationIdParamName, newValue);
+ }
+
+ if (command.isChangeInIntegerParameterNamed(ClientApiConstants.legalFormIdParamName, client.getLegalForm())) {
+ final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName);
+ if (newValue != null) {
+ LegalForm legalForm = LegalForm.fromInt(newValue);
+ if (legalForm != null) {
+ actualChanges.put(ClientApiConstants.legalFormIdParamName, ClientEnumerations.legalForm(newValue));
+ client.setLegalForm(legalForm.getValue());
+ client.resetDerivedNames(legalForm);
+ } else {
+ actualChanges.put(ClientApiConstants.legalFormIdParamName, null);
+ client.setLegalForm(null);
+ }
+ } else {
+ actualChanges.put(ClientApiConstants.legalFormIdParamName, null);
+ client.setLegalForm(null);
+ }
+ }
+
+ final String dateFormatAsInput = command.dateFormat();
+ final String localeAsInput = command.locale();
+
+ if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.activationDateParamName, client.getActivationDate())) {
+ final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.activationDateParamName);
+ actualChanges.put(ClientApiConstants.activationDateParamName, valueAsInput);
+ actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
+
+ client.setActivationDate(command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName));
+ client.setOfficeJoiningDate(client.getActivationDate());
+ }
+
+ if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.dateOfBirthParamName, client.getDateOfBirth())) {
+ final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName);
+ actualChanges.put(ClientApiConstants.dateOfBirthParamName, valueAsInput);
+ actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
+
+ client.setDateOfBirth(command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName));
+ }
+
+ if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.submittedOnDateParamName, client.getSubmittedOnDate())) {
+ final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
+ actualChanges.put(ClientApiConstants.submittedOnDateParamName, valueAsInput);
+ actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
+
+ client.setSubmittedOnDate(command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName));
+ }
+
+ // validateUpdate();
Review Comment:
This should not be commented out!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041084358
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testUpdateClientUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = ClientHelper.getBasicClientAsJSON(null, ClientHelper.LEGALFORM_ID_PERSON, clientExternalId);
+ final PutClientsClientIdResponse clientUpdateResponse = ClientHelper.updateClient(requestSpec, responseSpec, clientExternalId,
+ jsonPayload);
+ // then
+ assertNotNull(clientUpdateResponse);
+ assertNotNull(clientUpdateResponse.getResourceExternalId());
+ assertEquals(clientExternalId, clientUpdateResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testDeleteClientUsingExternalId() {
+ // given
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+
+ // then
+ final DeleteClientsClientIdResponse clientDeleteResponse = ClientHelper.deleteClient(requestSpec, responseSpec, clientExternalId);
+ assertNotNull(clientDeleteResponse);
+ assertNotNull(clientDeleteResponse.getResourceExternalId());
+ assertEquals(clientExternalId, clientDeleteResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testGetClientAccountsUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+
+ GetClientsClientIdAccountsResponse clientAccountsResponse = ClientHelper.getClientAccounts(requestSpec, responseSpec,
+ clientExternalId);
+
+ // then
+ assertNotNull(clientAccountsResponse);
Review Comment:
The accounts does not returns any client data, It just returns arrays of loan accounts or savings accounts, etc
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041911963
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -358,7 +324,7 @@ private void validate() {
}
- private void validateUpdate() {
+ public void validateUpdate() {
Review Comment:
you might wanna move this method into the `ClientDomainServiceImpl`. it is not used anywhere else.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1047148112
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Please undo the changes you done in the `LoanAccountData` and only change the `clientExternalId` as i asked. Please do not change the loan externalid as it was not part of this story and we will conflict.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044480018
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -732,6 +718,20 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
+ @Override
+ public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
Review Comment:
There are different (old) Client mapper for covering different flows, individual client, group client, etc. In this case It was not renamed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040888121
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientRepositoryWrapper.java:
##########
@@ -86,8 +87,21 @@ public Client getActiveClientInUserScope(Long clientId) {
public Client getClientByAccountNumber(String accountNumber) {
Client client = this.repository.getClientByAccountNumber(accountNumber);
if (client == null) {
- throw new ClientNotFoundException(accountNumber);
+ throw new ClientNotFoundException(accountNumber, "account.number");
}
return client;
}
+
+ public Client getClientByClientId(final Long clientId, final String hierarchySearchString) {
Review Comment:
Wrong method name. It get clients by cliendId and hierarchy, no?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041066866
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1042269698
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -463,7 +459,7 @@ public boolean isWithdrawn() {
return ClientStatus.fromInt(this.status).isWithdrawn();
}
- public Map<String, Object> update(final JsonCommand command) {
+ public Map<String, Object> update(final ExternalId externalId, final JsonCommand command) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1042242044
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientDomainServiceImpl.java:
##########
@@ -0,0 +1,225 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.domain;
+
+import java.time.LocalDate;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.office.domain.Office;
+import org.apache.fineract.organisation.staff.domain.Staff;
+import org.apache.fineract.portfolio.client.api.ClientApiConstants;
+import org.apache.fineract.portfolio.group.domain.Group;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ClientDomainServiceImpl implements ClientDomainService {
+
+ @Override
+ public Client createClient(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff,
+ final Long savingsProductId, final CodeValue gender, final CodeValue clientType, final CodeValue clientClassification,
+ final Integer legalForm, final ExternalId externalId, final JsonCommand command) {
+
+ final String accountNo = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
+ final String mobileNo = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
+ final String emailAddress = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
+
+ final String firstname = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
+ final String middlename = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
+ final String lastname = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
+ final String fullname = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
+
+ final boolean isStaff = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.isStaffParamName);
+
+ final LocalDate dataOfBirth = command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName);
+
+ ClientStatus status = ClientStatus.PENDING;
+ boolean active = false;
+ if (command.hasParameter("active")) {
+ active = command.booleanPrimitiveValueOfParameterNamed(ClientApiConstants.activeParamName);
+ }
+
+ LocalDate activationDate = null;
+ LocalDate officeJoiningDate = null;
+ if (active) {
+ status = ClientStatus.ACTIVE;
+ activationDate = command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName);
+ officeJoiningDate = activationDate;
+ }
+
+ LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
+ if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) {
+ submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
+ }
+ if (active && submittedOnDate.isAfter(activationDate)) {
+ submittedOnDate = activationDate;
+ }
+ final Long savingsAccountId = null;
+
+ return Client.instance(currentUser, status, clientOffice, clientParentGroup, accountNo, firstname, middlename, lastname, fullname,
+ activationDate, officeJoiningDate, externalId, mobileNo, emailAddress, staff, submittedOnDate, savingsProductId,
+ savingsAccountId, dataOfBirth, gender, clientType, clientClassification, legalForm, isStaff);
+
+ }
+
+ @Override
+ public Map<String, Object> updateClient(Client client, final ExternalId externalId, final JsonCommand command) {
+
+ final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
+
+ if (command.isChangeInIntegerParameterNamed(ClientApiConstants.statusParamName, client.getStatus())) {
+ final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.statusParamName);
+ actualChanges.put(ClientApiConstants.statusParamName, ClientEnumerations.status(newValue));
+ client.setStatus(ClientStatus.fromInt(newValue).getValue());
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.accountNoParamName, client.getAccountNumber())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.accountNoParamName);
+ actualChanges.put(ClientApiConstants.accountNoParamName, newValue);
+ client.setAccountNumber(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.externalIdParamName, externalId.getValue())) {
+ actualChanges.put(ClientApiConstants.externalIdParamName, externalId.getValue());
+ client.setExternalId(externalId);
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.mobileNoParamName, client.getMobileNo())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.mobileNoParamName);
+ actualChanges.put(ClientApiConstants.mobileNoParamName, newValue);
+ client.setMobileNo(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.emailAddressParamName, client.getEmailAddress())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.emailAddressParamName);
+ actualChanges.put(ClientApiConstants.emailAddressParamName, newValue);
+ client.setEmailAddress(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.firstnameParamName, client.getFirstname())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.firstnameParamName);
+ actualChanges.put(ClientApiConstants.firstnameParamName, newValue);
+ client.setFirstname(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.middlenameParamName, client.getMiddlename())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.middlenameParamName);
+ actualChanges.put(ClientApiConstants.middlenameParamName, newValue);
+ client.setMiddlename(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.lastnameParamName, client.getLastname())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.lastnameParamName);
+ actualChanges.put(ClientApiConstants.lastnameParamName, newValue);
+ client.setLastname(StringUtils.defaultIfEmpty(newValue, null));
+ }
+
+ if (command.isChangeInStringParameterNamed(ClientApiConstants.fullnameParamName, client.getFullname())) {
+ final String newValue = command.stringValueOfParameterNamed(ClientApiConstants.fullnameParamName);
+ actualChanges.put(ClientApiConstants.fullnameParamName, newValue);
+ client.setFullname(newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.staffIdParamName, client.staffId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.staffIdParamName);
+ actualChanges.put(ClientApiConstants.staffIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.genderIdParamName, client.genderId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.genderIdParamName);
+ actualChanges.put(ClientApiConstants.genderIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.savingsProductIdParamName, client.savingsProductId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.savingsProductIdParamName);
+ actualChanges.put(ClientApiConstants.savingsProductIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.clientTypeIdParamName, client.clientTypeId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientTypeIdParamName);
+ actualChanges.put(ClientApiConstants.clientTypeIdParamName, newValue);
+ }
+
+ if (command.isChangeInLongParameterNamed(ClientApiConstants.clientClassificationIdParamName, client.clientClassificationId())) {
+ final Long newValue = command.longValueOfParameterNamed(ClientApiConstants.clientClassificationIdParamName);
+ actualChanges.put(ClientApiConstants.clientClassificationIdParamName, newValue);
+ }
+
+ if (command.isChangeInIntegerParameterNamed(ClientApiConstants.legalFormIdParamName, client.getLegalForm())) {
+ final Integer newValue = command.integerValueOfParameterNamed(ClientApiConstants.legalFormIdParamName);
+ if (newValue != null) {
+ LegalForm legalForm = LegalForm.fromInt(newValue);
+ if (legalForm != null) {
+ actualChanges.put(ClientApiConstants.legalFormIdParamName, ClientEnumerations.legalForm(newValue));
+ client.setLegalForm(legalForm.getValue());
+ client.resetDerivedNames(legalForm);
+ } else {
+ actualChanges.put(ClientApiConstants.legalFormIdParamName, null);
+ client.setLegalForm(null);
+ }
+ } else {
+ actualChanges.put(ClientApiConstants.legalFormIdParamName, null);
+ client.setLegalForm(null);
+ }
+ }
+
+ final String dateFormatAsInput = command.dateFormat();
+ final String localeAsInput = command.locale();
+
+ if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.activationDateParamName, client.getActivationDate())) {
+ final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.activationDateParamName);
+ actualChanges.put(ClientApiConstants.activationDateParamName, valueAsInput);
+ actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
+
+ client.setActivationDate(command.localDateValueOfParameterNamed(ClientApiConstants.activationDateParamName));
+ client.setOfficeJoiningDate(client.getActivationDate());
+ }
+
+ if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.dateOfBirthParamName, client.getDateOfBirth())) {
+ final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName);
+ actualChanges.put(ClientApiConstants.dateOfBirthParamName, valueAsInput);
+ actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
+
+ client.setDateOfBirth(command.localDateValueOfParameterNamed(ClientApiConstants.dateOfBirthParamName));
+ }
+
+ if (command.isChangeInLocalDateParameterNamed(ClientApiConstants.submittedOnDateParamName, client.getSubmittedOnDate())) {
+ final String valueAsInput = command.stringValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
+ actualChanges.put(ClientApiConstants.submittedOnDateParamName, valueAsInput);
+ actualChanges.put(ClientApiConstants.dateFormatParamName, dateFormatAsInput);
+ actualChanges.put(ClientApiConstants.localeParamName, localeAsInput);
+
+ client.setSubmittedOnDate(command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName));
+ }
+
+ // validateUpdate();
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041079491
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
Done
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040266949
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResourceSwagger.java:
##########
@@ -252,6 +252,8 @@ private GetClientsGroups() {}
public List<GetClientsGroups> groups;
@Schema(example = "test@test.com")
public String emailAddress;
+ @Schema(example = "123")
+ public String externalId;
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039265325
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java:
##########
@@ -489,4 +491,11 @@ public boolean isExternalIdAutoGenerationEnabled() {
final GlobalConfigurationPropertyData property = getGlobalConfigurationPropertyData(ENABLE_EXTERNAL_ID_AUTO_GENERATION);
return property.isEnabled();
}
+
Review Comment:
How does this change relates to external id enhancement?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039275106
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResourceSwagger.java:
##########
@@ -252,6 +252,8 @@ private GetClientsGroups() {}
public List<GetClientsGroups> groups;
@Schema(example = "test@test.com")
public String emailAddress;
+ @Schema(example = "123")
+ public String externalId;
Review Comment:
I think more than 2 API should retrieve the external id as well. It should be retrieved in all cases (where it makes sense). Like: GetClientsResponse
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039608506
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java:
##########
@@ -60,4 +60,7 @@ public interface ClientReadPlatformService {
LocalDate retrieveClientTransferProposalDate(Long clientId);
void validateClient(Long clientId);
+
+ Long retrieveLoanIdByExternalId(String externalId);
Review Comment:
Done updated to `retrieveClientIdByExternalId`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039791113
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/client/ClientPersonImportHandler.java:
##########
@@ -95,7 +97,7 @@ private ClientData readClient(final Workbook workbook, final Row row, final Stri
if (staffId == 0L) {
staffId = null;
}
- String externalId = ImportHandlerUtils.readAsString(ClientPersonConstants.EXTERNAL_ID_COL, row);
+ ExternalId externalId = ExternalIdFactory.produce(ImportHandlerUtils.readAsString(ClientPersonConstants.EXTERNAL_ID_COL, row));
Review Comment:
Would it make more sense to use ExternalIdFactory.create()? it would auto generate one if the configuration is enabled and value was not provided
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039886014
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/importhandler/client/ClientEntityImportHandler.java:
##########
@@ -141,7 +143,7 @@ private ClientData readClient(final Workbook workbook, final Row row, final Stri
ClientNonPersonData clientNonPersonData = ClientNonPersonData.importInstance(incorporationNo, incorporationTill, remarks,
mainBusinessId, constitutionId, locale, dateFormat);
- String externalId = ImportHandlerUtils.readAsString(ClientEntityConstants.EXTERNAL_ID_COL, row);
+ ExternalId externalId = ExternalIdFactory.produce(ImportHandlerUtils.readAsString(ClientEntityConstants.EXTERNAL_ID_COL, row));
Review Comment:
Done!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040893967
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
You know the value of the external id, so please do equality check instead of null check here
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
Review Comment:
You know the value of the external id, so please do equality check instead of null check here
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041063786
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
Review Comment:
No, It can not be private, because It is being used by the Self service API resource
<img width="1099" alt="Screenshot 2022-12-06 at 8 41 14" src="https://user-images.githubusercontent.com/44206706/205941913-81052e7e-4a00-42c7-b01f-a93412716695.png">
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041060275
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/populator/AbstractWorkbookPopulator.java:
##########
@@ -130,7 +130,7 @@ protected void setClientAndGroupDateLookupTable(Sheet sheet, List<ClientData> cl
}
if (containsClientExtId) {
if (client.getExternalId() != null) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040842842
##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/bulkimport/populator/AbstractWorkbookPopulator.java:
##########
@@ -130,7 +130,7 @@ protected void setClientAndGroupDateLookupTable(Sheet sheet, List<ClientData> cl
}
if (containsClientExtId) {
if (client.getExternalId() != null) {
Review Comment:
This should be changed to `!client.getExternalId().isEmpty()`, no?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040849840
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
Review Comment:
externalId can be final
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
Review Comment:
externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044529090
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
- sqlBuilder.append(
+ builder.append(
"c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, ");
- sqlBuilder.append(
+ builder.append(
"cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, ");
- sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
- sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
- sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
- sqlBuilder.append("c.mobile_no as mobileNo, ");
- sqlBuilder.append("c.is_staff as isStaff, ");
- sqlBuilder.append("c.email_address as emailAddress, ");
- sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
- sqlBuilder.append("c.gender_cv_id as genderId, ");
- sqlBuilder.append("cv.code_value as genderValue, ");
- sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
- sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
- sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
- sqlBuilder.append("cvclassification.code_value as classificationValue, ");
- sqlBuilder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
- sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
- sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
- sqlBuilder.append("c.default_savings_account as savingsAccountId, ");
-
- sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
- sqlBuilder.append("sbu.username as submittedByUsername, ");
- sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
- sqlBuilder.append("sbu.lastname as submittedByLastname, ");
+ builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
+ builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
+ builder.append("c.fullname as fullname, c.display_name as displayName, ");
+ builder.append("c.mobile_no as mobileNo, ");
+ builder.append("c.is_staff as isStaff, ");
+ builder.append("c.email_address as emailAddress, ");
+ builder.append("c.date_of_birth as dateOfBirth, ");
+ builder.append("c.gender_cv_id as genderId, ");
+ builder.append("cv.code_value as genderValue, ");
+ builder.append("c.client_type_cv_id as clienttypeId, ");
+ builder.append("cvclienttype.code_value as clienttypeValue, ");
+ builder.append("c.client_classification_cv_id as classificationId, ");
+ builder.append("cvclassification.code_value as classificationValue, ");
+ builder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.closedon_date as closedOnDate, ");
- sqlBuilder.append("clu.username as closedByUsername, ");
- sqlBuilder.append("clu.firstname as closedByFirstname, ");
- sqlBuilder.append("clu.lastname as closedByLastname, ");
+ builder.append("c.submittedon_date as submittedOnDate, ");
+ builder.append("sbu.username as submittedByUsername, ");
+ builder.append("sbu.firstname as submittedByFirstname, ");
+ builder.append("sbu.lastname as submittedByLastname, ");
- sqlBuilder.append("acu.username as activatedByUsername, ");
- sqlBuilder.append("acu.firstname as activatedByFirstname, ");
- sqlBuilder.append("acu.lastname as activatedByLastname, ");
+ builder.append("c.closedon_date as closedOnDate, ");
+ builder.append("clu.username as closedByUsername, ");
+ builder.append("clu.firstname as closedByFirstname, ");
+ builder.append("clu.lastname as closedByLastname, ");
- sqlBuilder.append("cnp.constitution_cv_id as constitutionId, ");
- sqlBuilder.append("cvConstitution.code_value as constitutionValue, ");
- sqlBuilder.append("cnp.incorp_no as incorpNo, ");
- sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
- sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
- sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
- sqlBuilder.append("cnp.remarks as remarks ");
+ // builder.append("c.submittedon as submittedOnDate, ");
+ builder.append("acu.username as activatedByUsername, ");
+ builder.append("acu.firstname as activatedByFirstname, ");
+ builder.append("acu.lastname as activatedByLastname, ");
- sqlBuilder.append("from m_client c ");
- sqlBuilder.append("join m_office o on o.id = c.office_id ");
- sqlBuilder.append("left join m_client_non_person cnp on cnp.client_id = c.id ");
- sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id ");
Review Comment:
That line is still there in the code this is part of other method that maps a `ClientData` but in this case for Member of Groups
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on PR #2783:
URL: https://github.com/apache/fineract/pull/2783#issuecomment-1352812250
@josehernandezfintecheandomx I just realised during the bulk import we might need to add proper serializer for ExternalId.
Please add the `gsonBuilder.registerTypeAdapter(ExternalId.class, new ExternalIdAdapter());` in: `ClientEntityImportHandler.importEntity()`
`ClientPersonImportHandler.importEntity()`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041320902
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java:
##########
@@ -60,4 +61,7 @@ public interface ClientReadPlatformService {
LocalDate retrieveClientTransferProposalDate(Long clientId);
void validateClient(Long clientId);
+
Review Comment:
This is a legacy code, but anyway, removed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1042815557
##########
gradle.properties:
##########
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
+org.gradle.jvmargs=-Xmx4g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
Review Comment:
As we discussed It's generating an issue
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039264757
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java:
##########
@@ -91,6 +84,29 @@ public AccountSummaryCollectionData retrieveClientAccountDetails(final Long clie
return new AccountSummaryCollectionData(loanAccounts, glimAccounts, savingsAccounts, shareAccounts, guarantorloanAccounts);
}
+ @Override
+ public AccountSummaryCollectionData retrieveClientAccountDetails(String externalId) {
Review Comment:
We discussed we dont need this.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039285495
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -763,30 +757,18 @@ public boolean isStaff() {
return this.isStaff;
}
- public String getExternalId() {
- return this.externalId;
+ public void setExternalId(String externalId) {
Review Comment:
It should use ExternalId as type
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039293985
##########
gradle.properties:
##########
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
+org.gradle.jvmargs=-Xmx4g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
Review Comment:
is this really necessary? i am not really comfortable of this change
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039293985
##########
gradle.properties:
##########
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
+org.gradle.jvmargs=-Xmx4g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
Review Comment:
is this really necessary?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1045871843
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
@josehernandezfintecheandomx
Please use the ExternalId object here and modify the `LoanAccountData.clientExternalId` to be ExternalId type.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044527454
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
- sqlBuilder.append(
+ builder.append(
"c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, ");
- sqlBuilder.append(
+ builder.append(
"cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, ");
- sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
- sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
- sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
- sqlBuilder.append("c.mobile_no as mobileNo, ");
- sqlBuilder.append("c.is_staff as isStaff, ");
- sqlBuilder.append("c.email_address as emailAddress, ");
- sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
- sqlBuilder.append("c.gender_cv_id as genderId, ");
- sqlBuilder.append("cv.code_value as genderValue, ");
- sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
- sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
- sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
- sqlBuilder.append("cvclassification.code_value as classificationValue, ");
- sqlBuilder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
- sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
- sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
- sqlBuilder.append("c.default_savings_account as savingsAccountId, ");
-
- sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
- sqlBuilder.append("sbu.username as submittedByUsername, ");
- sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
- sqlBuilder.append("sbu.lastname as submittedByLastname, ");
+ builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
+ builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
+ builder.append("c.fullname as fullname, c.display_name as displayName, ");
+ builder.append("c.mobile_no as mobileNo, ");
+ builder.append("c.is_staff as isStaff, ");
+ builder.append("c.email_address as emailAddress, ");
+ builder.append("c.date_of_birth as dateOfBirth, ");
+ builder.append("c.gender_cv_id as genderId, ");
+ builder.append("cv.code_value as genderValue, ");
+ builder.append("c.client_type_cv_id as clienttypeId, ");
+ builder.append("cvclienttype.code_value as clienttypeValue, ");
+ builder.append("c.client_classification_cv_id as classificationId, ");
+ builder.append("cvclassification.code_value as classificationValue, ");
+ builder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.closedon_date as closedOnDate, ");
- sqlBuilder.append("clu.username as closedByUsername, ");
- sqlBuilder.append("clu.firstname as closedByFirstname, ");
- sqlBuilder.append("clu.lastname as closedByLastname, ");
+ builder.append("c.submittedon_date as submittedOnDate, ");
+ builder.append("sbu.username as submittedByUsername, ");
+ builder.append("sbu.firstname as submittedByFirstname, ");
+ builder.append("sbu.lastname as submittedByLastname, ");
- sqlBuilder.append("acu.username as activatedByUsername, ");
- sqlBuilder.append("acu.firstname as activatedByFirstname, ");
- sqlBuilder.append("acu.lastname as activatedByLastname, ");
+ builder.append("c.closedon_date as closedOnDate, ");
+ builder.append("clu.username as closedByUsername, ");
+ builder.append("clu.firstname as closedByFirstname, ");
+ builder.append("clu.lastname as closedByLastname, ");
- sqlBuilder.append("cnp.constitution_cv_id as constitutionId, ");
- sqlBuilder.append("cvConstitution.code_value as constitutionValue, ");
- sqlBuilder.append("cnp.incorp_no as incorpNo, ");
- sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
- sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
- sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
- sqlBuilder.append("cnp.remarks as remarks ");
+ // builder.append("c.submittedon as submittedOnDate, ");
Review Comment:
This commented line is part of the original code, I mean I did not commented out this line.
<img width="919" alt="Screenshot 2022-12-09 at 8 49 00" src="https://user-images.githubusercontent.com/44206706/206728300-c128ee07-b2c2-4767-acb7-9a9d674b14a4.png">
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1046234696
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1043556557
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -358,7 +324,7 @@ private void validate() {
}
- private void validateUpdate() {
+ public void validateUpdate() {
Review Comment:
Yes, but It has a call to the client method `validateActivationDate` and this is used in other part inside of `Client`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039284331
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -34,82 +36,83 @@
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.address.data.AddressData;
-import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
/**
* Immutable data object representing client data.
*/
+@NoArgsConstructor
@Getter
+@Setter
public final class ClientData implements Comparable<ClientData>, Serializable {
- private final Long id;
- private final String accountNo;
- private final String externalId;
-
- private final EnumOptionData status;
- private final CodeValueData subStatus;
-
- private final Boolean active;
- private final LocalDate activationDate;
-
- private final String firstname;
- private final String middlename;
- private final String lastname;
- private final String fullname;
- private final String displayName;
- private final String mobileNo;
- private final String emailAddress;
- private final LocalDate dateOfBirth;
- private final CodeValueData gender;
- private final CodeValueData clientType;
- private final CodeValueData clientClassification;
- private final Boolean isStaff;
-
- private final Long officeId;
- private final String officeName;
- private final Long transferToOfficeId;
- private final String transferToOfficeName;
-
- private final Long imageId;
- private final Boolean imagePresent;
- private final Long staffId;
- private final String staffName;
- private final ClientTimelineData timeline;
-
- private final Long savingsProductId;
- private final String savingsProductName;
-
- private final Long savingsAccountId;
- private final EnumOptionData legalForm;
- private final Set<ClientCollateralManagementData> clientCollateralManagements;
+ private Long id;
+ private String accountNo;
+ private String externalId;
Review Comment:
We should use ExternalId as a type here...
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039302792
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ private void fetchClientByExternalId(final String externalId) {
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, externalId);
+ assertNotNull(clientResponse);
+ assertEquals(externalId, clientResponse.getExternalId());
+ }
+}
Review Comment:
Missing test cases:
- Retrieve all client and client details contains the external id
- Update client by external id and result contains the externalId
- Delete client by external id
- Do actions on client by external id (activate, close, reactive, undo, ...) all these actions are retrieving the externalId in the result
- Fetch client account by externalId
- Fetch obligee details by externalId
- Fetch transfer proposal date by externalId
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039287520
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientRepository.java:
##########
@@ -29,4 +29,35 @@ interface ClientRepository extends JpaRepository<Client, Long>, JpaSpecification
@Query(FIND_CLIENT_BY_ACCOUNT_NUMBER)
Client getClientByAccountNumber(@Param("accountNumber") String accountNumber);
+
+ @Query("""
+ SELECT client.id
+ FROM Client client
+ WHERE client.externalId = :externalId
+ """)
+ Long getClientIdByExternalId(String externalId);
+
+ @Query("""
+ SELECT client
+ FROM Client client
+ JOIN client.office office
+ LEFT JOIN client.transferToOffice transferToOffice
+ WHERE client.id = :clientId
+ AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
+ """)
+ Client fetchByClientIdAndHierarchy(Long clientId, String officeHierarchy, String transferToOfficeHierarchy);
+
+ @Query("""
+ SELECT client
+ FROM Client client
+ JOIN client.office office
+ LEFT JOIN client.transferToOffice transferToOffice
+ WHERE client.externalId = :externalId
+ AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
+ """)
+ Client fetchByExternalIdAndHierarchy(String externalId, String officeHierarchy, String transferToOfficeHierarchy);
+
+ @Query("SELECT c.id FROM Client c WHERE c.externalId = :externalId")
+ Long findIdByExternalId(@Param("externalId") String externalId);
Review Comment:
What is the difference between this method and "getClientIdByExternalId" ?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040850456
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final AccountSummaryCollectionData clientAccount = accountDetailsReadPlatformService.retrieveClientAccountDetails(clientId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return clientAccountSummaryToApiJsonSerializer.serialize(settings, clientAccount,
+ ClientApiConstants.CLIENT_ACCOUNTS_DATA_PARAMETERS);
+ }
+
+ private String applyCommandOverClient(Long clientId, String externalId, final String command, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
Review Comment:
externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040886870
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -763,30 +750,18 @@ public boolean isStaff() {
return this.isStaff;
}
- public String getExternalId() {
- return this.externalId;
+ public void setExternalId(ExternalId externalId) {
Review Comment:
There should be no setter for external id. Why do we need this?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040894809
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testUpdateClientUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = ClientHelper.getBasicClientAsJSON(null, ClientHelper.LEGALFORM_ID_PERSON, clientExternalId);
+ final PutClientsClientIdResponse clientUpdateResponse = ClientHelper.updateClient(requestSpec, responseSpec, clientExternalId,
+ jsonPayload);
+ // then
+ assertNotNull(clientUpdateResponse);
+ assertNotNull(clientUpdateResponse.getResourceExternalId());
Review Comment:
You know the value of the external id, so please do equality check instead of null check here
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testUpdateClientUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = ClientHelper.getBasicClientAsJSON(null, ClientHelper.LEGALFORM_ID_PERSON, clientExternalId);
+ final PutClientsClientIdResponse clientUpdateResponse = ClientHelper.updateClient(requestSpec, responseSpec, clientExternalId,
+ jsonPayload);
+ // then
+ assertNotNull(clientUpdateResponse);
+ assertNotNull(clientUpdateResponse.getResourceExternalId());
+ assertEquals(clientExternalId, clientUpdateResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testDeleteClientUsingExternalId() {
+ // given
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+
+ // then
+ final DeleteClientsClientIdResponse clientDeleteResponse = ClientHelper.deleteClient(requestSpec, responseSpec, clientExternalId);
+ assertNotNull(clientDeleteResponse);
+ assertNotNull(clientDeleteResponse.getResourceExternalId());
Review Comment:
You know the value of the external id, so please do equality check instead of null check here
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040891345
##########
gradle.properties:
##########
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-org.gradle.jvmargs=-Xmx2g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
+org.gradle.jvmargs=-Xmx4g --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-exports=java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.naming/javax.naming=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=null
Review Comment:
Please undo this...
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040889924
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java:
##########
@@ -60,4 +61,7 @@ public interface ClientReadPlatformService {
LocalDate retrieveClientTransferProposalDate(Long clientId);
void validateClient(Long clientId);
+
Review Comment:
We dont need this
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] galovics commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
galovics commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040923233
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -545,90 +543,77 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
- @Override
- public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
-
- final AppUser currentUser = this.context.authenticatedUser();
- final String hierarchy = currentUser.getOffice().getHierarchy();
- final String hierarchySearchString = hierarchy + "%";
-
- final String sql = "select " + this.membersOfGroupMapper.schema()
- + " left join m_group g on pgc.group_id=g.id where o.hierarchy like ? and g.parent_id = ? and c.status_enum = ? group by c.id";
-
- return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, // NOSONAR
- hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue());
- }
-
- private static final class ClientMapper implements RowMapper<ClientData> {
+ private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMapper() {
- final StringBuilder builder = new StringBuilder(400);
+ ClientMembersOfGroupMapper() {
+ final StringBuilder sqlBuilder = new StringBuilder(200);
Review Comment:
Please revert this back to it's original name. It's just polluting the PR with unnecessary changes which makes it really hard to review.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
Review Comment:
Please put this back to it's original place. It's just polluting the PR with unnecessary changes which makes it really hard to review.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java:
##########
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.mapper;
+
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.client.data.ClientData;
+import org.apache.fineract.portfolio.client.data.ClientTimelineData;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+@Mapper(config = MapstructMapperConfig.class)
+public interface ClientMapper {
+
+ @Mapping(target = "accountNo", source = "source.accountNumber")
+ @Mapping(target = "status", source = "source", qualifiedByName = "clientStatusEnum")
+ @Mapping(target = "subStatus", source = "source", qualifiedByName = "clientSubStatusCode")
+ @Mapping(target = "officeId", source = "source.office.id")
+ @Mapping(target = "officeName", source = "source.office.name")
+ @Mapping(target = "transferToOfficeId", source = "source.transferToOffice.id")
+ @Mapping(target = "transferToOfficeName", source = "source.transferToOffice.name")
+ @Mapping(target = "externalId", source = "source.externalId")
+ @Mapping(target = "gender", source = "source", qualifiedByName = "clientGenderCode")
+ @Mapping(target = "imageId", source = "source.image.id")
+ @Mapping(target = "staffId", source = "source.staff.id")
+ @Mapping(target = "staffName", source = "source.staff.displayName")
+ @Mapping(target = "timeline", source = "source", qualifiedByName = "clientTimeloneData")
+ @Mapping(target = "savingsProductId", source = "source.savingsProductId")
+ @Mapping(target = "savingsProductName", source = "source.id")
+ @Mapping(target = "savingsAccountId", source = "source.savingsAccountId")
+ @Mapping(target = "clientType", source = "source", qualifiedByName = "clientTypeCode")
+ @Mapping(target = "clientClassification", source = "source", qualifiedByName = "clientClassificationCode")
+ @Mapping(target = "legalForm", source = "source", qualifiedByName = "clientLegalFormEnum")
+ @Mapping(target = "isStaff", source = "source", qualifiedByName = "clientIsStaff")
+ @Mapping(target = "imagePresent", ignore = true)
+ @Mapping(target = "officeOptions", ignore = true)
+ @Mapping(target = "staffOptions", ignore = true)
+ @Mapping(target = "narrations", ignore = true)
+ @Mapping(target = "savingProductOptions", ignore = true)
+ @Mapping(target = "savingAccountOptions", ignore = true)
+ @Mapping(target = "genderOptions", ignore = true)
+ @Mapping(target = "clientTypeOptions", ignore = true)
+ @Mapping(target = "clientClassificationOptions", ignore = true)
+ @Mapping(target = "clientNonPersonConstitutionOptions", ignore = true)
+ @Mapping(target = "clientNonPersonMainBusinessLineOptions", ignore = true)
+ @Mapping(target = "clientLegalFormOptions", ignore = true)
+ @Mapping(target = "familyMemberOptions", ignore = true)
+ @Mapping(target = "clientNonPersonDetails", ignore = true)
+ @Mapping(target = "address", ignore = true)
+ @Mapping(target = "isAddressEnabled", ignore = true)
+ @Mapping(target = "datatables", ignore = true)
+ @Mapping(target = "rowIndex", ignore = true)
+ @Mapping(target = "dateFormat", ignore = true)
+ @Mapping(target = "locale", ignore = true)
+ @Mapping(target = "clientTypeId", ignore = true)
+ @Mapping(target = "genderId", ignore = true)
+ @Mapping(target = "clientClassificationId", ignore = true)
+ @Mapping(target = "legalFormId", ignore = true)
+ @Mapping(target = "clientCollateralManagements", ignore = true)
+ @Mapping(target = "groups", ignore = true)
+ ClientData map(Client source);
+
+ @Named("clientTypeCode")
+ default CodeValueData clientTypeCode(Client client) {
+ final CodeValue code = client.getClientType();
+ if (code == null) {
+ return null;
+ }
+ return CodeValueData.instance(code.getId(), code.getLabel());
+ }
+
+ @Named("clientClassificationCode")
+ default CodeValueData clientClassificationCode(Client client) {
+ final CodeValue code = client.getClientClassification();
+ if (code == null) {
+ return null;
+ }
+ return CodeValueData.instance(code.getId(), code.getLabel());
+ }
+
+ @Named("clientSubStatusCode")
+ default CodeValueData clientSubStatusCode(Client client) {
+ final CodeValue code = client.getSubStatus();
+ if (code == null) {
+ return null;
+ }
+ return CodeValueData.instance(code.getId(), code.getLabel());
+ }
+
+ @Named("clientGenderCode")
+ default CodeValueData clientGenderCode(Client client) {
+ final CodeValue code = client.getGender();
+ if (code == null) {
+ return null;
+ }
+ return CodeValueData.instance(code.getId(), code.getLabel());
+ }
+
+ @Named("clientLegalFormEnum")
+ default EnumOptionData clientLegalFormEnum(Client client) {
+ return ClientEnumerations.legalForm(client.getLegalForm());
+ }
+
+ @Named("clientStatusEnum")
+ default EnumOptionData clientStatusEnum(Client client) {
+ return ClientEnumerations.status(client.getStatus());
+ }
+
+ @Named("clientTimeloneData")
Review Comment:
Typo.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -732,6 +718,20 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
+ @Override
+ public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
Review Comment:
Please put this back to it's original place. It's just polluting the PR with unnecessary changes which makes it really hard to review.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
Review Comment:
Please revert this back to it's original name. It's just polluting the PR with unnecessary changes which makes it really hard to review.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/mapper/ClientMapper.java:
##########
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.client.mapper;
+
+import org.apache.fineract.infrastructure.codes.data.CodeValueData;
+import org.apache.fineract.infrastructure.codes.domain.CodeValue;
+import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
+import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.portfolio.client.data.ClientData;
+import org.apache.fineract.portfolio.client.data.ClientTimelineData;
+import org.apache.fineract.portfolio.client.domain.Client;
+import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
+import org.apache.fineract.useradministration.domain.AppUser;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.Named;
+
+@Mapper(config = MapstructMapperConfig.class)
+public interface ClientMapper {
+
+ @Mapping(target = "accountNo", source = "source.accountNumber")
+ @Mapping(target = "status", source = "source", qualifiedByName = "clientStatusEnum")
+ @Mapping(target = "subStatus", source = "source", qualifiedByName = "clientSubStatusCode")
+ @Mapping(target = "officeId", source = "source.office.id")
+ @Mapping(target = "officeName", source = "source.office.name")
+ @Mapping(target = "transferToOfficeId", source = "source.transferToOffice.id")
+ @Mapping(target = "transferToOfficeName", source = "source.transferToOffice.name")
+ @Mapping(target = "externalId", source = "source.externalId")
+ @Mapping(target = "gender", source = "source", qualifiedByName = "clientGenderCode")
+ @Mapping(target = "imageId", source = "source.image.id")
+ @Mapping(target = "staffId", source = "source.staff.id")
+ @Mapping(target = "staffName", source = "source.staff.displayName")
+ @Mapping(target = "timeline", source = "source", qualifiedByName = "clientTimeloneData")
+ @Mapping(target = "savingsProductId", source = "source.savingsProductId")
+ @Mapping(target = "savingsProductName", source = "source.id")
+ @Mapping(target = "savingsAccountId", source = "source.savingsAccountId")
+ @Mapping(target = "clientType", source = "source", qualifiedByName = "clientTypeCode")
+ @Mapping(target = "clientClassification", source = "source", qualifiedByName = "clientClassificationCode")
+ @Mapping(target = "legalForm", source = "source", qualifiedByName = "clientLegalFormEnum")
+ @Mapping(target = "isStaff", source = "source", qualifiedByName = "clientIsStaff")
+ @Mapping(target = "imagePresent", ignore = true)
Review Comment:
So why do we ignore these props if they are present in the ClientData class?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039581083
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/accountdetails/service/AccountDetailsReadPlatformServiceJpaRepositoryImpl.java:
##########
@@ -91,6 +84,29 @@ public AccountSummaryCollectionData retrieveClientAccountDetails(final Long clie
return new AccountSummaryCollectionData(loanAccounts, glimAccounts, savingsAccounts, shareAccounts, guarantorloanAccounts);
}
+ @Override
+ public AccountSummaryCollectionData retrieveClientAccountDetails(String externalId) {
Review Comment:
Done removed!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039605644
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientRepository.java:
##########
@@ -29,4 +29,35 @@ interface ClientRepository extends JpaRepository<Client, Long>, JpaSpecification
@Query(FIND_CLIENT_BY_ACCOUNT_NUMBER)
Client getClientByAccountNumber(@Param("accountNumber") String accountNumber);
+
+ @Query("""
+ SELECT client.id
+ FROM Client client
+ WHERE client.externalId = :externalId
+ """)
+ Long getClientIdByExternalId(String externalId);
+
+ @Query("""
+ SELECT client
+ FROM Client client
+ JOIN client.office office
+ LEFT JOIN client.transferToOffice transferToOffice
+ WHERE client.id = :clientId
+ AND (office.hierarchy LIKE :officeHierarchy OR transferToOffice.hierarchy LIKE :transferToOfficeHierarchy)
+ """)
+ Client fetchByClientIdAndHierarchy(Long clientId, String officeHierarchy, String transferToOfficeHierarchy);
+
+ @Query("""
Review Comment:
Done removed!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039793234
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -293,7 +297,7 @@ public static ClientData template(final Long officeId, final LocalDate joinedDat
final String lastname = null;
final String fullname = null;
final String displayName = null;
- final String externalId = null;
+ final ExternalId externalId = null;
Review Comment:
ExternalId type object cannot be allowed to be null! Use `ExternalId.empty()`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039811298
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -24,92 +24,96 @@
import java.util.List;
import java.util.Set;
import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.dataqueries.data.DatatableData;
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.staff.data.StaffData;
import org.apache.fineract.portfolio.address.data.AddressData;
-import org.apache.fineract.portfolio.collateralmanagement.domain.ClientCollateralManagement;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
import org.apache.fineract.portfolio.savings.data.SavingsProductData;
/**
* Immutable data object representing client data.
*/
+@NoArgsConstructor
@Getter
+@Setter
public final class ClientData implements Comparable<ClientData>, Serializable {
- private final Long id;
- private final String accountNo;
- private final String externalId;
-
- private final EnumOptionData status;
- private final CodeValueData subStatus;
-
- private final Boolean active;
- private final LocalDate activationDate;
-
- private final String firstname;
- private final String middlename;
- private final String lastname;
- private final String fullname;
- private final String displayName;
- private final String mobileNo;
- private final String emailAddress;
- private final LocalDate dateOfBirth;
- private final CodeValueData gender;
- private final CodeValueData clientType;
- private final CodeValueData clientClassification;
- private final Boolean isStaff;
-
- private final Long officeId;
- private final String officeName;
- private final Long transferToOfficeId;
- private final String transferToOfficeName;
-
- private final Long imageId;
- private final Boolean imagePresent;
- private final Long staffId;
- private final String staffName;
- private final ClientTimelineData timeline;
-
- private final Long savingsProductId;
- private final String savingsProductName;
-
- private final Long savingsAccountId;
- private final EnumOptionData legalForm;
- private final Set<ClientCollateralManagementData> clientCollateralManagements;
+ private Long id;
+ private String accountNo;
+ private ExternalId externalId;
Review Comment:
Please add a manual getter for this, which return `ExternalId.empty()` if it is null.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] galovics merged pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
galovics merged PR #2783:
URL: https://github.com/apache/fineract/pull/2783
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1047148112
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java:
##########
@@ -1485,7 +1486,7 @@ public LoanAccountData retrieveClientDetailsTemplate(final Long clientId) {
final LocalDate expectedDisbursementDate = DateUtils.getBusinessLocalDate();
return LoanAccountData.clientDefaults(clientAccount.getId(), clientAccount.getAccountNo(), clientAccount.getDisplayName(),
- clientAccount.getOfficeId(), clientAccount.getExternalId(), expectedDisbursementDate);
+ clientAccount.getOfficeId(), clientAccount.getExternalId().getValue(), expectedDisbursementDate);
Review Comment:
Please undo the changes you done in the `LoanAccountData` and **only** change the `clientExternalId` as i asked. Please do not change the loan externalid as it was not part of this story and we will conflict.
You have modified the `externalId` field which should leave AS-IS, it's part of my story to change that and you not changed the `clientExternalId` which should be part of your story. Please undo these latest changes and change only the `clientExternalId` field in the `LoanAccountData`
@josehernandezfintecheandomx is that okay?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] galovics commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
galovics commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1048940880
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -559,11 +559,143 @@ public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long cen
hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMapper implements RowMapper<ClientData> {
+ private static final class ParentGroupsMapper implements RowMapper<GroupGeneralData> {
Review Comment:
Why has this (and the below classes) been reordered?
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/AccountNumberGenerator.java:
##########
@@ -165,7 +153,7 @@ private String generateAccountNumber(Map<String, String> propertyMap, AccountNum
prefix = prefix.substring(0, Math.min(prefix.length(), 10));
}
if (accountNumberPrefixType.getValue().equals(AccountNumberPrefixType.PREFIX_SHORT_NAME.getValue())) {
- Integer prefixLength = prefix.length();
+ Integer prefixLength = prefix == null ? 10 : prefix.length();
Review Comment:
Is this needed for the externalId work? Either revert this or cover with tests.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039804761
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -555,6 +523,7 @@ public CommandProcessingResult updateClient(final Long clientId, final JsonComma
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
+ .withEntityExternalId(clientForUpdate.getExternalId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
Review Comment:
Please make sure the rest of the methods are returning the externalId as well.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039662977
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -763,30 +757,18 @@ public boolean isStaff() {
return this.isStaff;
}
- public String getExternalId() {
- return this.externalId;
+ public void setExternalId(String externalId) {
Review Comment:
Done, updated!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039890596
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientData.java:
##########
@@ -293,7 +297,7 @@ public static ClientData template(final Long officeId, final LocalDate joinedDat
final String lastname = null;
final String fullname = null;
final String displayName = null;
- final String externalId = null;
+ final ExternalId externalId = null;
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040265338
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
Review Comment:
This is for having a clear way or difference between the Integration Test cases and most important, the new TCs are using the Fineract client
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040265769
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -555,6 +523,7 @@ public CommandProcessingResult updateClient(final Long clientId, final JsonComma
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
+ .withEntityExternalId(clientForUpdate.getExternalId()) //
.withOfficeId(clientForUpdate.officeId()) //
.withClientId(clientId) //
.withEntityId(clientId) //
Review Comment:
Done, It's included in the TCs as assertion
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040265923
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java:
##########
@@ -290,6 +247,16 @@ public CommandProcessingResult createClient(final JsonCommand command) {
final Client newClient = Client.createNew(currentUser, clientOffice, clientParentGroup, staff, savingsProductId, gender,
clientType, clientClassification, legalFormValue, command);
+
+ final String externalIdValue = command.stringValueOfParameterNamed(ClientApiConstants.externalIdParamName);
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client and External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1039288976
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformService.java:
##########
@@ -60,4 +60,7 @@ public interface ClientReadPlatformService {
LocalDate retrieveClientTransferProposalDate(Long clientId);
void validateClient(Long clientId);
+
+ Long retrieveLoanIdByExternalId(String externalId);
Review Comment:
This is misplaced i believe. The Client service should not resolve Loan related data...
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1044479372
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
Review Comment:
There are different (old) Client mapper for covering different flows, individual client, group client, etc. In this case It was not renamed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041066272
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040850214
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
+ ClientData clientData = clientReadPlatformService.retrieveOne(clientId);
+ if (isTemplate) {
+ final ClientData templateData = clientReadPlatformService.retrieveTemplate(clientData.getOfficeId(), staffInSelectedOfficeOnly);
+ clientData = ClientData.templateOnTop(clientData, templateData);
+ Collection<SavingsAccountData> savingAccountOptions = savingsAccountReadPlatformService.retrieveForLookup(clientId, null);
+ if (savingAccountOptions != null && savingAccountOptions.size() > 0) {
+ clientData = ClientData.templateWithSavingAccountOptions(clientData, savingAccountOptions);
+ }
+ }
+ return clientData;
+ }
+
+ private Long getResolvedClientId(final Long clientId, final ExternalId clientExternalId) {
+ Long resolvedClientId = clientId;
+ if (resolvedClientId == null) {
+ clientExternalId.throwExceptionIfEmpty();
+ resolvedClientId = clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId);
+ if (resolvedClientId == null) {
+ throw new ClientNotFoundException(resolvedClientId);
+ }
+ }
+ return resolvedClientId;
+ }
+
+ private CommandWrapper evaluateCommand(Long clientId, String commandParam, CommandWrapperBuilder builder) {
+ CommandWrapper commandRequest = null;
+ if (CommandParameterUtil.is(commandParam, "activate")) {
+ commandRequest = builder.activateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "assignStaff")) {
+ commandRequest = builder.assignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "unassignStaff")) {
+ commandRequest = builder.unassignClientStaff(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "close")) {
+ commandRequest = builder.closeClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeTransfer")) {
+ commandRequest = builder.proposeClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "proposeAndAcceptTransfer")) {
+ commandRequest = builder.proposeAndAcceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdrawTransfer")) {
+ commandRequest = builder.withdrawClientTransferRequest(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "acceptTransfer")) {
+ commandRequest = builder.acceptClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "rejectTransfer")) {
+ commandRequest = builder.rejectClientTransfer(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "updateSavingsAccount")) {
+ commandRequest = builder.updateClientSavingsAccount(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reject")) {
+ commandRequest = builder.rejectClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "withdraw")) {
+ commandRequest = builder.withdrawClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "reactivate")) {
+ commandRequest = builder.reActivateClient(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoRejection")) {
+ commandRequest = builder.undoRejection(clientId).build();
+ } else if (CommandParameterUtil.is(commandParam, "undoWithdrawal")) {
+ commandRequest = builder.undoWithdrawal(clientId).build();
+ }
+
+ if (commandRequest == null) {
+ throw new UnrecognizedQueryParamException("command", commandParam,
+ new Object[] { "activate", "unassignStaff", "assignStaff", "close", "proposeTransfer", "withdrawTransfer",
+ "acceptTransfer", "rejectTransfer", "updateSavingsAccount", "reject", "withdraw", "reactivate" });
+ }
+
+ return commandRequest;
+ }
+
+ private String retrieveClient(Long clientId, String externalId, final boolean staffInSelectedOfficeOnly, final UriInfo uriInfo) {
+ context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ final ClientData clientData = retrieveClientData(clientId, staffInSelectedOfficeOnly, settings.isTemplate());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private String updateClient(Long clientId, String externalId, final String jsonPayload) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .updateClient(clientId) //
+ .withJson(jsonPayload) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String deleteClient(Long clientId, String externalId) {
+ ExternalId clientExternalId = ExternalIdFactory.produce(externalId);
+ clientId = getResolvedClientId(clientId, clientExternalId);
+
+ final CommandWrapper commandRequest = new CommandWrapperBuilder() //
+ .deleteClient(clientId) //
+ .build(); //
+
+ final CommandProcessingResult result = commandsSourceWritePlatformService.logCommandSource(commandRequest);
+ return toApiJsonSerializer.serialize(result);
+ }
+
+ private String retrieveClientAccounts(Long clientId, String externalId, final UriInfo uriInfo) {
Review Comment:
externalId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040846935
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientsApiResource.java:
##########
@@ -415,25 +300,302 @@ public String postClientTemplate(@QueryParam("legalFormType") final String legal
@GET
@Path("{clientId}/obligeedetails")
- @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
public String retrieveObligeeDetails(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(clientId, null);
+ }
- context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
-
- final List<ObligeeData> ObligeeList = guarantorReadPlatformService.retrieveObligeeDetails(clientId);
+ @GET
+ @Path("{clientId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(clientId, null);
+ }
- return toApiJsonSerializer.serialize(ObligeeList);
+ @GET
+ @Path("/external-id/{externalId}")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve a Client by External Id", description = "Example Requests:\n" + "\n" + "clients/123-456\n" + "\n" + "\n"
+ + "clients/123-456?template=true\n" + "\n" + "\n" + "clients/123-456?fields=id,displayName,officeName")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdResponse.class))) })
+ public String retrieveOne(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo,
+ @DefaultValue("false") @QueryParam("staffInSelectedOfficeOnly") @Parameter(description = "staffInSelectedOfficeOnly") final boolean staffInSelectedOfficeOnly) {
+ return retrieveClient(null, externalId, staffInSelectedOfficeOnly, uriInfo);
}
@GET
- @Path("{clientId}/transferproposaldate")
+ @Path("/external-id/{externalId}/accounts")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client accounts overview", description = "An example of how a loan portfolio summary can be provided. This is requested in a specific use case of the community application.\n"
+ + "It is quite reasonable to add resources like this to simplify User Interface development.\n" + "\n" + "Example Requests:\n "
+ + "\n" + "clients/123-456/accounts\n" + "\n" + "clients/123-456/accounts?fields=loanAccounts,savingsAccounts")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientsClientIdAccountsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveAssociatedAccounts(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @Context final UriInfo uriInfo) {
+ return retrieveClientAccounts(null, externalId, uriInfo);
+ }
+
+ @PUT
+ @Path("/external-id/{externalId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
- public String retrieveTransferTemplate(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+ @Operation(summary = "Update a Client using the External Id", description = "Note: You can update any of the basic attributes of a client (but not its associations) using this API.\n"
+ + "\n"
+ + "Changing the relationship between a client and its office is not supported through this API. An API specific to handling transfers of clients between offices is available for the same.\n"
+ + "\n" + "The relationship between a client and a group must be removed through the Groups API.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PutClientsClientIdResponse.class))) })
+ public String update(@Parameter(description = "externalId") @PathParam("externalId") final String externalId,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return updateClient(null, externalId, apiRequestBodyAsJson);
+ }
+
+ @POST
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Activate a Client | Close a Client | Reject a Client | Withdraw a Client | Reactivate a Client | UndoReject a Client | UndoWithdraw a Client | Assign a Staff | Unassign a Staff | Update Default Savings Account | Propose a Client Transfer | Withdraw a Client Transfer | Reject a Client Transfer | Accept a Client Transfer | Propose and Accept a Client Transfer", description = "Activate a Client:\n\n"
+ + "Clients can be created in a Pending state. This API exists to enable client activation (for when a client becomes an approved member of the financial Institution).\n"
+ + "\n" + "If the client happens to be already active this API will result in an error.\n\n" + "Close a Client:\n\n"
+ + "Clients can be closed if they do not have any non-closed loans/savingsAccount. This API exists to close a client .\n" + "\n"
+ + "If the client have any active loans/savingsAccount this API will result in an error.\n\n" + "Reject a Client:\n\n"
+ + "Clients can be rejected when client is in pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n" + "Mandatory Fields: rejectionDate, rejectionReasonId\n\n"
+ + "Withdraw a Client:\n\n" + "Client applications can be withdrawn when client is in a pending for activation status.\n" + "\n"
+ + "If the client is any other status, this API throws an error.\n\n"
+ + "Mandatory Fields: withdrawalDate, withdrawalReasonId\n\n"
+ + "Reactivate a Client: Clients can be reactivated after they have been closed.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reactivationDate\n\n"
+ + "UndoReject a Client:\n\n" + "Clients can be reactivated after they have been rejected.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate"
+ + "UndoWithdraw a Client:\n\n" + "Clients can be reactivated after they have been withdrawn.\n" + "\n"
+ + "Trying to reactivate a client in any other state throws an error.\n\n" + "Mandatory Fields: reopenedDate\n\n"
+ + "Assign a Staff:\n\n" + "Allows you to assign a Staff for existed Client.\n" + "\n"
+ + "The selected Staff should belong to the same office (or an officer higher up in the hierarchy) as the Client he manages.\n\n"
+ + "Unassign a Staff:\n\n" + "Allows you to unassign the Staff assigned to a Client.\n\n" + "Update Default Savings Account:\n\n"
+ + "Allows you to modify or assign a default savings account for an existing Client.\n" + "\n"
+ + "The selected savings account should be one among the existing savings account for a particular customer.\n\n"
+ + "Propose a Client Transfer:\n\n" + "Allows you to propose the transfer of a Client to a different Office.\n\n"
+ + "Withdraw a Client Transfer:\n\n" + "Allows you to withdraw the proposed transfer of a Client to a different Office.\n" + "\n"
+ + "Withdrawal can happen only if the destination Branch (to which the transfer was proposed) has not already accepted the transfer proposal\n\n"
+ + "Reject a Client Transfer:\n\n" + "Allows the Destination Branch to reject the proposed Client Transfer.\n\n"
+ + "Accept a Client Transfer:\n\n" + "Allows the Destination Branch to accept the proposed Client Transfer.\n" + "\n"
+ + "The destination branch may also choose to link this client to a group (in which case, any existing active JLG loan of the client is rescheduled to match the meeting frequency of the group) and loan Officer at the time of accepting the transfer\n\n"
+ + "Propose and Accept a Client Transfer:\n\n"
+ + "Abstraction over the Propose and Accept Client Transfer API's which enable a user with Data Scope over both the Target and Destination Branches to directly transfer a Client to the destination Office.\n\n"
+ + "Showing request/response for 'Reject a Client Transfer'")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.PostClientsClientIdResponse.class))) })
+ public String applyCommand(@PathParam("externalId") @Parameter(description = "externalId") final String externalId,
+ @QueryParam("command") @Parameter(description = "command") final String commandParam,
+ @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+ return applyCommandOverClient(null, externalId, commandParam, apiRequestBodyAsJson);
+ }
+
+ @DELETE
+ @Path("/external-id/{externalId}")
+ @Consumes({ MediaType.APPLICATION_JSON })
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Delete a Client", description = "If a client is in Pending state, you are allowed to Delete it. The delete is a 'hard delete' and cannot be recovered from. Once clients become active or have loans or savings associated with them, you cannot delete the client but you may Close the client if they have left the program.")
+ @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdRequest.class)))
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.DeleteClientsClientIdResponse.class))) })
+ public String delete(@PathParam("externalId") @Parameter(description = "externalId") final String externalId) {
+ return deleteClient(null, externalId);
+ }
+
+ @GET
+ @Path("/external-id/{externalId}/obligeedetails")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client obligee details", description = "Retrieve client obligee details using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientObligeeDetailsResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveObligeeDetails(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientObligeeDetails(null, externalId);
+ }
+ @GET
+ @Path("/external-id/{externalId}/transferproposaldate")
+ @Produces({ MediaType.APPLICATION_JSON })
+ @Operation(summary = "Retrieve client transfer template", description = "Retrieve client transfer template using the client external Id")
+ @ApiResponses({
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientsApiResourceSwagger.GetClientTransferProposalDateResponse.class))),
+ @ApiResponse(responseCode = "400", description = "Bad Request") })
+ public String retrieveTransferTemplate(@PathParam("externalId") final String externalId, @Context final UriInfo uriInfo) {
+ return retrieveClientTransferTemplate(null, externalId);
+ }
+
+ public String retrieveAll(final UriInfo uriInfo, final String sqlSearch, final Long officeId, final String externalId,
+ final String displayName, final String firstname, final String lastname, final String status, final String hierarchy,
+ final Integer offset, final Integer limit, final String orderBy, final String sortOrder, final Boolean orphansOnly,
+ final boolean isSelfUser) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_RESOURCE_NAME);
+ final SearchParameters searchParameters = SearchParameters.forClients(sqlSearch, officeId, externalId, displayName, firstname,
+ lastname, status, hierarchy, offset, limit, orderBy, sortOrder, orphansOnly, isSelfUser);
+ final Page<ClientData> clientData = clientReadPlatformService.retrieveAll(searchParameters);
+ final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+ return toApiJsonSerializer.serialize(settings, clientData, ClientApiConstants.CLIENT_RESPONSE_DATA_PARAMETERS);
+ }
+
+ private ClientData retrieveClientData(Long clientId, final boolean staffInSelectedOfficeOnly, final boolean isTemplate) {
Review Comment:
clientId can be final
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] adamsaghy commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
adamsaghy commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1040885753
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/Client.java:
##########
@@ -224,12 +227,11 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom {
@OneToMany(mappedBy = "client", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<ClientCollateralManagement> clientCollateralManagements = new HashSet<>();
- public static Client createNew(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff,
+ public static Client instance(final AppUser currentUser, final Office clientOffice, final Group clientParentGroup, final Staff staff,
final Long savingsProductId, final CodeValue gender, final CodeValue clientType, final CodeValue clientClassification,
- final Integer legalForm, final JsonCommand command) {
+ final Integer legalForm, final ExternalId externalId, final JsonCommand command) {
Review Comment:
Pls move this whole logic into the service. Inside an entity we should not fetch data out of command. The command parameter should never be reach an entity. it should be processed in the service.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041080198
##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientExternalIdTest.java:
##########
@@ -0,0 +1,320 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.DeleteClientsClientIdResponse;
+import org.apache.fineract.client.models.GetClientTransferProposalDateResponse;
+import org.apache.fineract.client.models.GetClientsClientIdAccountsResponse;
+import org.apache.fineract.client.models.GetClientsClientIdResponse;
+import org.apache.fineract.client.models.GetObligeeData;
+import org.apache.fineract.client.models.PostClientsClientIdResponse;
+import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PutClientsClientIdResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.portfolio.client.domain.ClientStatus;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class ClientExternalIdTest {
+
+ private ResponseSpecification responseSpec;
+ private RequestSpecification requestSpec;
+
+ @BeforeEach
+ public void setup() {
+ Utils.initializeRESTAssured();
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNull(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOffCreateClientWithValue() {
+ // given
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClient() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(36, clientResponse.getResourceExternalId().length());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void whenAutoExternalIdConfigIsOnCreateClientWithValue() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ final String externalId = Utils.randomStringGenerator("ID_", 7);
+ final String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON,
+ externalId);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ // then
+ assertNotNull(clientResponse);
+ assertNotNull(clientResponse.getResourceExternalId());
+ assertEquals(externalId, clientResponse.getResourceExternalId());
+
+ fetchClientByExternalId(clientResponse.getResourceExternalId());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testClientStatusUsingExternalId() {
+ ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ final PostClientsResponse addClientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = addClientResponse.getResourceExternalId();
+ final Long clientId = addClientResponse.getClientId();
+ assertNotNull(clientExternalId);
+ log.info("Client data id {} and external Id {}", clientId, clientExternalId);
+
+ GetClientsClientIdResponse clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Close Client action
+ jsonPayload = clientHelper.getCloseClientAsJSON();
+ PostClientsClientIdResponse commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec,
+ clientExternalId, ClientHelper.CLOSE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.CLOSED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reactivate Client action
+ jsonPayload = clientHelper.getReactivateClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.PENDING, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Reject Client action
+ jsonPayload = clientHelper.getRejectClientAsJSON();
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.REJECT_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.REJECTED, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ // Activate Client action
+ jsonPayload = ClientHelper.getActivateClientAsJSON(ClientHelper.DEFAULT_DATE);
+ commandResponse = ClientHelper.performClientActionUsingExternalId(requestSpec, responseSpec, clientExternalId,
+ ClientHelper.ACTIVATE_CLIENT_COMMAND, jsonPayload);
+ assertNotNull(commandResponse);
+ assertNotNull(commandResponse.getResourceExternalId());
+ log.info("Client data id {} and external Id {}", commandResponse.getResourceId(), clientExternalId);
+ assertEquals(clientId.intValue(), commandResponse.getResourceId());
+
+ clientResponse = ClientHelper.getClientByExternalId(requestSpec, responseSpec, clientExternalId);
+ ClientStatusChecker.verifyClientStatus(ClientStatus.ACTIVE, clientResponse);
+ log.info("Client data id {} and status {}", clientExternalId, clientResponse.getStatus().getCode());
+
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, false);
+ }
+
+ @Test
+ public void testUpdateClientUsingExternalId() {
+ // given
+ GlobalConfigurationHelper.manageConfigurations(requestSpec, responseSpec,
+ GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+ String jsonPayload = ClientHelper.getBasicClientAsJSON(ClientHelper.DEFAULT_OFFICE_ID, ClientHelper.LEGALFORM_ID_PERSON, null);
+ // when
+ final PostClientsResponse clientResponse = ClientHelper.addClientAsPerson(requestSpec, responseSpec, jsonPayload);
+ final String clientExternalId = clientResponse.getResourceExternalId();
+ jsonPayload = ClientHelper.getBasicClientAsJSON(null, ClientHelper.LEGALFORM_ID_PERSON, clientExternalId);
+ final PutClientsClientIdResponse clientUpdateResponse = ClientHelper.updateClient(requestSpec, responseSpec, clientExternalId,
+ jsonPayload);
+ // then
+ assertNotNull(clientUpdateResponse);
+ assertNotNull(clientUpdateResponse.getResourceExternalId());
Review Comment:
Done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1041075520
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientRepositoryWrapper.java:
##########
@@ -86,8 +87,21 @@ public Client getActiveClientInUserScope(Long clientId) {
public Client getClientByAccountNumber(String accountNumber) {
Client client = this.repository.getClientByAccountNumber(accountNumber);
if (client == null) {
- throw new ClientNotFoundException(accountNumber);
+ throw new ClientNotFoundException(accountNumber, "account.number");
}
return client;
}
+
+ public Client getClientByClientId(final Long clientId, final String hierarchySearchString) {
Review Comment:
Done, updated
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] galovics commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
galovics commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1045521379
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
Review Comment:
The variable was renamed. Please undo it.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
Review Comment:
I never said this was renamed. The method was reordered within the class. Please put it back.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -732,6 +718,20 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
+ @Override
+ public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
Review Comment:
It's been reordered, please undo it.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
- sqlBuilder.append(
+ builder.append(
"c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, ");
- sqlBuilder.append(
+ builder.append(
"cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, ");
- sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
- sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
- sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
- sqlBuilder.append("c.mobile_no as mobileNo, ");
- sqlBuilder.append("c.is_staff as isStaff, ");
- sqlBuilder.append("c.email_address as emailAddress, ");
- sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
- sqlBuilder.append("c.gender_cv_id as genderId, ");
- sqlBuilder.append("cv.code_value as genderValue, ");
- sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
- sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
- sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
- sqlBuilder.append("cvclassification.code_value as classificationValue, ");
- sqlBuilder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
- sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
- sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
- sqlBuilder.append("c.default_savings_account as savingsAccountId, ");
-
- sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
- sqlBuilder.append("sbu.username as submittedByUsername, ");
- sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
- sqlBuilder.append("sbu.lastname as submittedByLastname, ");
+ builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
+ builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
+ builder.append("c.fullname as fullname, c.display_name as displayName, ");
+ builder.append("c.mobile_no as mobileNo, ");
+ builder.append("c.is_staff as isStaff, ");
+ builder.append("c.email_address as emailAddress, ");
+ builder.append("c.date_of_birth as dateOfBirth, ");
+ builder.append("c.gender_cv_id as genderId, ");
+ builder.append("cv.code_value as genderValue, ");
+ builder.append("c.client_type_cv_id as clienttypeId, ");
+ builder.append("cvclienttype.code_value as clienttypeValue, ");
+ builder.append("c.client_classification_cv_id as classificationId, ");
+ builder.append("cvclassification.code_value as classificationValue, ");
+ builder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.closedon_date as closedOnDate, ");
- sqlBuilder.append("clu.username as closedByUsername, ");
- sqlBuilder.append("clu.firstname as closedByFirstname, ");
- sqlBuilder.append("clu.lastname as closedByLastname, ");
+ builder.append("c.submittedon_date as submittedOnDate, ");
+ builder.append("sbu.username as submittedByUsername, ");
+ builder.append("sbu.firstname as submittedByFirstname, ");
+ builder.append("sbu.lastname as submittedByLastname, ");
- sqlBuilder.append("acu.username as activatedByUsername, ");
- sqlBuilder.append("acu.firstname as activatedByFirstname, ");
- sqlBuilder.append("acu.lastname as activatedByLastname, ");
+ builder.append("c.closedon_date as closedOnDate, ");
+ builder.append("clu.username as closedByUsername, ");
+ builder.append("clu.firstname as closedByFirstname, ");
+ builder.append("clu.lastname as closedByLastname, ");
- sqlBuilder.append("cnp.constitution_cv_id as constitutionId, ");
- sqlBuilder.append("cvConstitution.code_value as constitutionValue, ");
- sqlBuilder.append("cnp.incorp_no as incorpNo, ");
- sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
- sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
- sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
- sqlBuilder.append("cnp.remarks as remarks ");
+ // builder.append("c.submittedon as submittedOnDate, ");
Review Comment:
In terms of what the diff shows, this wasn't part of the query. If this is due to reordering again, please undo it and I can check it properly.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -545,90 +543,77 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
- @Override
- public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
-
- final AppUser currentUser = this.context.authenticatedUser();
- final String hierarchy = currentUser.getOffice().getHierarchy();
- final String hierarchySearchString = hierarchy + "%";
-
- final String sql = "select " + this.membersOfGroupMapper.schema()
- + " left join m_group g on pgc.group_id=g.id where o.hierarchy like ? and g.parent_id = ? and c.status_enum = ? group by c.id";
-
- return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, // NOSONAR
- hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue());
- }
-
- private static final class ClientMapper implements RowMapper<ClientData> {
+ private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMapper() {
- final StringBuilder builder = new StringBuilder(400);
+ ClientMembersOfGroupMapper() {
+ final StringBuilder sqlBuilder = new StringBuilder(200);
Review Comment:
The variable was renamed. Please undo it.
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -370,77 +305,141 @@ public Collection<ClientData> retrieveActiveClientMembersOfGroup(final Long grou
hierarchySearchString, groupId, ClientStatus.ACTIVE.getValue());
}
- private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
+ private String buildSqlStringFromClientCriteria(String schemaSql, final SearchParameters searchParameters, List<Object> paramList) {
+
+ String sqlSearch = searchParameters.getSqlSearch();
+ final Long officeId = searchParameters.getOfficeId();
+ final String externalId = searchParameters.getExternalId();
+ final String displayName = searchParameters.getName();
+ final String firstname = searchParameters.getFirstname();
+ final String lastname = searchParameters.getLastname();
+ final String status = searchParameters.getStatus();
+
+ String extraCriteria = "";
+ if (sqlSearch != null) {
+ sqlSearch = sqlSearch.replaceAll(" display_name ", " c.display_name ");
+ sqlSearch = sqlSearch.replaceAll("display_name ", "c.display_name ");
+ extraCriteria = " and (" + sqlSearch + ")";
+ this.columnValidator.validateSqlInjection(schemaSql, sqlSearch);
+ }
+
+ if (officeId != null) {
+ extraCriteria += " and c.office_id = ? ";
+ paramList.add(officeId);
+ }
+
+ if (externalId != null) {
+ paramList.add(externalId);
+ extraCriteria += " and c.external_id like ? ";
+ }
+
+ if (displayName != null) {
+ // extraCriteria += " and concatcoalesce(c.firstname, ''),
+ // if(c.firstname > '',' ', '') , coalesce(c.lastname, '')) like "
+ paramList.add("%" + displayName + "%");
+ extraCriteria += " and c.display_name like ? ";
+ }
+
+ if (status != null) {
+ ClientStatus clientStatus = ClientStatus.fromString(status);
+ extraCriteria += " and c.status_enum = " + clientStatus.getValue().toString() + " ";
+ }
+
+ if (firstname != null) {
+ paramList.add(firstname);
+ extraCriteria += " and c.firstname like ? ";
+ }
+
+ if (lastname != null) {
+ paramList.add(lastname);
+ extraCriteria += " and c.lastname like ? ";
+ }
+
+ if (searchParameters.isScopedByOfficeHierarchy()) {
+ paramList.add(searchParameters.getHierarchy() + "%");
+ extraCriteria += " and o.hierarchy like ? ";
+ }
+
+ if (searchParameters.isOrphansOnly()) {
+ extraCriteria += " and c.id NOT IN (select client_id from m_group_client) ";
+ }
+
+ if (StringUtils.isNotBlank(extraCriteria)) {
+ extraCriteria = extraCriteria.substring(4);
+ }
+ return extraCriteria;
+ }
+
+ private static final class ClientToDataMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMembersOfGroupMapper() {
- final StringBuilder sqlBuilder = new StringBuilder(200);
+ ClientToDataMapper() {
+ final StringBuilder builder = new StringBuilder(400);
- sqlBuilder.append(
+ builder.append(
"c.id as id, c.account_no as accountNo, c.external_id as externalId, c.status_enum as statusEnum,c.sub_status as subStatus, ");
- sqlBuilder.append(
+ builder.append(
"cvSubStatus.code_value as subStatusValue,cvSubStatus.code_description as subStatusDesc,c.office_id as officeId, o.name as officeName, ");
- sqlBuilder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
- sqlBuilder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
- sqlBuilder.append("c.fullname as fullname, c.display_name as displayName, ");
- sqlBuilder.append("c.mobile_no as mobileNo, ");
- sqlBuilder.append("c.is_staff as isStaff, ");
- sqlBuilder.append("c.email_address as emailAddress, ");
- sqlBuilder.append("c.date_of_birth as dateOfBirth, ");
- sqlBuilder.append("c.gender_cv_id as genderId, ");
- sqlBuilder.append("cv.code_value as genderValue, ");
- sqlBuilder.append("c.client_type_cv_id as clienttypeId, ");
- sqlBuilder.append("cvclienttype.code_value as clienttypeValue, ");
- sqlBuilder.append("c.client_classification_cv_id as classificationId, ");
- sqlBuilder.append("cvclassification.code_value as classificationValue, ");
- sqlBuilder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.activation_date as activationDate, c.image_id as imageId, ");
- sqlBuilder.append("c.staff_id as staffId, s.display_name as staffName,");
- sqlBuilder.append("c.default_savings_product as savingsProductId, sp.name as savingsProductName, ");
- sqlBuilder.append("c.default_savings_account as savingsAccountId, ");
-
- sqlBuilder.append("c.submittedon_date as submittedOnDate, ");
- sqlBuilder.append("sbu.username as submittedByUsername, ");
- sqlBuilder.append("sbu.firstname as submittedByFirstname, ");
- sqlBuilder.append("sbu.lastname as submittedByLastname, ");
+ builder.append("c.transfer_to_office_id as transferToOfficeId, transferToOffice.name as transferToOfficeName, ");
+ builder.append("c.firstname as firstname, c.middlename as middlename, c.lastname as lastname, ");
+ builder.append("c.fullname as fullname, c.display_name as displayName, ");
+ builder.append("c.mobile_no as mobileNo, ");
+ builder.append("c.is_staff as isStaff, ");
+ builder.append("c.email_address as emailAddress, ");
+ builder.append("c.date_of_birth as dateOfBirth, ");
+ builder.append("c.gender_cv_id as genderId, ");
+ builder.append("cv.code_value as genderValue, ");
+ builder.append("c.client_type_cv_id as clienttypeId, ");
+ builder.append("cvclienttype.code_value as clienttypeValue, ");
+ builder.append("c.client_classification_cv_id as classificationId, ");
+ builder.append("cvclassification.code_value as classificationValue, ");
+ builder.append("c.legal_form_enum as legalFormEnum, ");
- sqlBuilder.append("c.closedon_date as closedOnDate, ");
- sqlBuilder.append("clu.username as closedByUsername, ");
- sqlBuilder.append("clu.firstname as closedByFirstname, ");
- sqlBuilder.append("clu.lastname as closedByLastname, ");
+ builder.append("c.submittedon_date as submittedOnDate, ");
+ builder.append("sbu.username as submittedByUsername, ");
+ builder.append("sbu.firstname as submittedByFirstname, ");
+ builder.append("sbu.lastname as submittedByLastname, ");
- sqlBuilder.append("acu.username as activatedByUsername, ");
- sqlBuilder.append("acu.firstname as activatedByFirstname, ");
- sqlBuilder.append("acu.lastname as activatedByLastname, ");
+ builder.append("c.closedon_date as closedOnDate, ");
+ builder.append("clu.username as closedByUsername, ");
+ builder.append("clu.firstname as closedByFirstname, ");
+ builder.append("clu.lastname as closedByLastname, ");
- sqlBuilder.append("cnp.constitution_cv_id as constitutionId, ");
- sqlBuilder.append("cvConstitution.code_value as constitutionValue, ");
- sqlBuilder.append("cnp.incorp_no as incorpNo, ");
- sqlBuilder.append("cnp.incorp_validity_till as incorpValidityTill, ");
- sqlBuilder.append("cnp.main_business_line_cv_id as mainBusinessLineId, ");
- sqlBuilder.append("cvMainBusinessLine.code_value as mainBusinessLineValue, ");
- sqlBuilder.append("cnp.remarks as remarks ");
+ // builder.append("c.submittedon as submittedOnDate, ");
+ builder.append("acu.username as activatedByUsername, ");
+ builder.append("acu.firstname as activatedByFirstname, ");
+ builder.append("acu.lastname as activatedByLastname, ");
- sqlBuilder.append("from m_client c ");
- sqlBuilder.append("join m_office o on o.id = c.office_id ");
- sqlBuilder.append("left join m_client_non_person cnp on cnp.client_id = c.id ");
- sqlBuilder.append("join m_group_client pgc on pgc.client_id = c.id ");
Review Comment:
In terms of what the diff shows, this isn't there If this is due to reordering again, please undo it and I can check it properly.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
[GitHub] [fineract] josehernandezfintecheandomx commented on a diff in pull request #2783: FINERACT-1760: Client by External Id
Posted by GitBox <gi...@apache.org>.
josehernandezfintecheandomx commented on code in PR #2783:
URL: https://github.com/apache/fineract/pull/2783#discussion_r1046086290
##########
fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientReadPlatformServiceImpl.java:
##########
@@ -545,90 +543,77 @@ public ClientData mapRow(final ResultSet rs, final int rowNum) throws SQLExcepti
}
}
- @Override
- public Collection<ClientData> retrieveActiveClientMembersOfCenter(final Long centerId) {
-
- final AppUser currentUser = this.context.authenticatedUser();
- final String hierarchy = currentUser.getOffice().getHierarchy();
- final String hierarchySearchString = hierarchy + "%";
-
- final String sql = "select " + this.membersOfGroupMapper.schema()
- + " left join m_group g on pgc.group_id=g.id where o.hierarchy like ? and g.parent_id = ? and c.status_enum = ? group by c.id";
-
- return this.jdbcTemplate.query(sql, this.membersOfGroupMapper, // NOSONAR
- hierarchySearchString, centerId, ClientStatus.ACTIVE.getValue());
- }
-
- private static final class ClientMapper implements RowMapper<ClientData> {
+ private static final class ClientMembersOfGroupMapper implements RowMapper<ClientData> {
private final String schema;
- ClientMapper() {
- final StringBuilder builder = new StringBuilder(400);
+ ClientMembersOfGroupMapper() {
+ final StringBuilder sqlBuilder = new StringBuilder(200);
Review Comment:
The variable never was renamed, Please take a look of the next screenshot to you can see the git diff is getting that difference...
<img width="1034" alt="Screenshot 2022-12-12 at 10 46 22" src="https://user-images.githubusercontent.com/44206706/207104159-1d323868-6f99-4f3c-a1f6-ad2def054f02.png">
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org