This is an automated email from the ASF dual-hosted git repository. arnold pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push: new 8fa1c4571 [FINERACT-1760] Client transaction by transaction external ID 8fa1c4571 is described below commit 8fa1c4571a634ab1705ad20fd7aa738c47dde69b Author: taskain7 <task...@gmail.com> AuthorDate: Fri Jul 21 10:20:10 2023 +0200 [FINERACT-1760] Client transaction by transaction external ID --- .../api/ClientChargesApiResourceSwagger.java | 2 + .../client/api/ClientTransactionsApiResource.java | 145 +++++++++++++++++++-- .../client/data/ClientApiCollectionConstants.java | 2 +- .../portfolio/client/domain/ClientTransaction.java | 14 +- .../client/domain/ClientTransactionRepository.java | 3 + .../client/exception/ClientNotFoundException.java | 5 + .../ClientTransactionNotFoundException.java | 7 + .../ClientChargeWritePlatformServiceImpl.java | 7 +- .../ClientTransactionReadPlatformService.java | 4 + .../ClientTransactionReadPlatformServiceImpl.java | 9 ++ .../client/ClientTransactionTest.java | 20 ++- .../integrationtests/common/ClientHelper.java | 35 ++++- 12 files changed, 229 insertions(+), 24 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResourceSwagger.java index cf9da1fb5..0769c0b38 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientChargesApiResourceSwagger.java @@ -162,6 +162,8 @@ final class ClientChargesApiResourceSwagger { public String dateFormat; @Schema(example = "01 September 2015") public String transactionDate; + @Schema(example = "3e7791ce-aa10-11ec-b909-0242ac120002") + public String externalId; } @Schema(description = "PostClientsClientIdChargesChargeIdResponse") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java index f70e8c1a3..27584344b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/api/ClientTransactionsApiResource.java @@ -43,6 +43,7 @@ import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; @@ -51,7 +52,9 @@ import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.portfolio.client.data.ClientTransactionData; +import org.apache.fineract.portfolio.client.domain.ClientTransaction; import org.apache.fineract.portfolio.client.exception.ClientNotFoundException; +import org.apache.fineract.portfolio.client.exception.ClientTransactionNotFoundException; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.client.service.ClientTransactionReadPlatformService; import org.springframework.stereotype.Component; @@ -122,7 +125,8 @@ public class ClientTransactionsApiResource { @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) @Operation(summary = "List Client Transactions", description = "The list capability of client transaction can support pagination." - + "\n\n" + "Example Requests:\n\n" + "clients/189/transactions\n\n" + "clients/189/transactions?offset=10&limit=50") + + "\n\n" + "Example Requests:\n\n" + "clients/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions\n\n" + + "clients/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions?offset=10&limit=50") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsResponse.class))) }) public String retrieveAllClientTransactions( @@ -131,9 +135,11 @@ public class ClientTransactionsApiResource { @QueryParam("limit") @Parameter(description = "limit") final Integer limit) { context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); - Long clientId = resolveClientId(clientExternalId); + ExternalId clientExtId = ExternalIdFactory.produce(clientExternalId); + + Long clientId = resolveClientId(clientExtId); if (Objects.isNull(clientId)) { - throw new ClientNotFoundException(); + throw new ClientNotFoundException(clientExtId); } return getAllClientTransactions(clientId, uriInfo, offset, limit); @@ -143,8 +149,9 @@ public class ClientTransactionsApiResource { @Path("external-id/{clientExternalId}/transactions/{transactionId}") @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - @Operation(summary = "Retrieve a Client Transaction", description = "Example Requests:\n" + "clients/1/transactions/1\n" + "\n" + "\n" - + "clients/1/transactions/1?fields=id,officeName") + @Operation(summary = "Retrieve a Client Transaction", description = "Example Requests:\n" + + "clients/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions/1\n" + "\n" + "\n" + + "clients/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions/1?fields=id,officeName") @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsTransactionIdResponse.class))) }) public String retrieveClientTransaction( @@ -154,10 +161,11 @@ public class ClientTransactionsApiResource { context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); - Long clientId = resolveClientId(clientExternalId); + ExternalId clientExtId = ExternalIdFactory.produce(clientExternalId); + Long clientId = resolveClientId(clientExtId); if (Objects.isNull(clientId)) { - throw new ClientNotFoundException(); + throw new ClientNotFoundException(clientExtId); } return getClientTransaction(clientId, transactionId, uriInfo); @@ -176,9 +184,119 @@ public class ClientTransactionsApiResource { @QueryParam("command") @Parameter(description = "command") final String commandParam, @Parameter(hidden = true) final String apiRequestBodyAsJson) { - Long clientId = resolveClientId(clientExternalId); + ExternalId clientExtId = ExternalIdFactory.produce(clientExternalId); + + Long clientId = resolveClientId(clientExtId); + if (Objects.isNull(clientId)) { + throw new ClientNotFoundException(clientExtId); + } + + return undoTransaction(clientId, transactionId, commandParam, apiRequestBodyAsJson); + } + + @GET + @Path("{clientId}/transactions/external-id/{transactionExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Retrieve a Client Transaction", description = "Example Requests:\n" + + "clients/1/transactions/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854\n" + "\n" + "\n" + + "clients/1/transactions/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854?fields=id,officeName") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsTransactionIdResponse.class))) }) + public String retrieveClientTransaction(@PathParam("clientId") @Parameter(description = "clientId") final Long clientId, + @PathParam("transactionExternalId") @Parameter(description = "transactionExternalId") final String transactionExternalId, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + ExternalId transactionExtId = ExternalIdFactory.produce(transactionExternalId); + + Long transactionId = resolveTransactionId(transactionExtId); + + if (Objects.isNull(transactionId)) { + throw new ClientTransactionNotFoundException(clientId, transactionExtId); + } + + return getClientTransaction(clientId, transactionId, uriInfo); + } + + @GET + @Path("external-id/{clientExternalId}/transactions/external-id/{transactionExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Retrieve a Client Transaction", description = "Example Requests:\n" + + "clients/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854\n" + + "\n" + "\n" + + "clients/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854/transactions/external-id/7dd80a7c-ycba-a446-t378-91eb6f53e854?fields=id,officeName") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsTransactionIdResponse.class))) }) + public String retrieveClientTransaction( + @PathParam("clientExternalId") @Parameter(description = "clientExternalId") final String clientExternalId, + @PathParam("transactionExternalId") @Parameter(description = "transactionExternalId") final String transactionExternalId, + @Context final UriInfo uriInfo) { + + context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); + + ExternalId clientExtId = ExternalIdFactory.produce(clientExternalId); + Long clientId = resolveClientId(clientExtId); + if (Objects.isNull(clientId)) { + throw new ClientNotFoundException(clientExtId); + } + + ExternalId transactionExtId = ExternalIdFactory.produce(transactionExternalId); + Long transactionId = resolveTransactionId(transactionExtId); + if (Objects.isNull(transactionId)) { + throw new ClientTransactionNotFoundException(clientId, transactionExtId); + } + + return getClientTransaction(clientId, transactionId, uriInfo); + } + + @POST + @Path("{clientId}/transactions/external-id/{transactionExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Undo a Client Transaction", description = "Undoes a Client Transaction") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.PostClientsClientIdTransactionsTransactionIdResponse.class))) }) + public String undoClientTransaction(@PathParam("clientId") @Parameter(description = "clientId") final Long clientId, + @PathParam("transactionExternalId") @Parameter(description = "transactionExternalId") final String transactionExternalId, + @QueryParam("command") @Parameter(description = "command") final String commandParam, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + + ExternalId transactionExtId = ExternalIdFactory.produce(transactionExternalId); + Long transactionId = resolveTransactionId(transactionExtId); + if (Objects.isNull(transactionId)) { + throw new ClientTransactionNotFoundException(clientId, transactionExtId); + } + + return undoTransaction(clientId, transactionId, commandParam, apiRequestBodyAsJson); + } + + @POST + @Path("external-id/{clientExternalId}/transactions/external-id/{transactionExternalId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + @Operation(summary = "Undo a Client Transaction", description = "Undoes a Client Transaction") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.PostClientsClientIdTransactionsTransactionIdResponse.class))) }) + public String undoClientTransaction( + @PathParam("clientExternalId") @Parameter(description = "clientExternalId") final String clientExternalId, + @PathParam("transactionExternalId") @Parameter(description = "transactionExternalId") final String transactionExternalId, + @QueryParam("command") @Parameter(description = "command") final String commandParam, + @Parameter(hidden = true) final String apiRequestBodyAsJson) { + + ExternalId clientExtId = ExternalIdFactory.produce(clientExternalId); + + Long clientId = resolveClientId(clientExtId); if (Objects.isNull(clientId)) { - throw new ClientNotFoundException(); + throw new ClientNotFoundException(clientExtId); + } + + ExternalId transactionExtId = ExternalIdFactory.produce(transactionExternalId); + Long transactionId = resolveTransactionId(transactionExtId); + if (Objects.isNull(transactionId)) { + throw new ClientTransactionNotFoundException(clientId, transactionExtId); } return undoTransaction(clientId, transactionId, commandParam, apiRequestBodyAsJson); @@ -214,8 +332,13 @@ public class ClientTransactionsApiResource { } } - private Long resolveClientId(String clientExternalId) { - return clientReadPlatformService.retrieveClientIdByExternalId(ExternalIdFactory.produce(clientExternalId)); + private Long resolveClientId(ExternalId clientExternalId) { + return clientReadPlatformService.retrieveClientIdByExternalId(clientExternalId); + } + + private Long resolveTransactionId(ExternalId transactionExternalId) { + ClientTransaction clientTransaction = clientTransactionReadPlatformService.retrieveTransactionByExternalId(transactionExternalId); + return !Objects.isNull(clientTransaction) ? clientTransaction.getId() : null; } private boolean is(final String commandParam, final String commandValue) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java index 2c7638101..330afe3e6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/data/ClientApiCollectionConstants.java @@ -78,6 +78,6 @@ public class ClientApiCollectionConstants extends ClientApiConstants { protected static final Set<String> CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(amountParamName, transactionDateParamName, dateFormatParamName, localeParamName, paymentTypeIdParamName, transactionAccountNumberParamName, - checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); + checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName, externalIdParamName)); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java index b940e030b..858375861 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransaction.java @@ -39,6 +39,7 @@ import java.util.Set; import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; @@ -81,7 +82,7 @@ public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom { private boolean reversed; @Column(name = "external_id", length = 100, nullable = true, unique = true) - private String externalId; + private ExternalId externalId; /* * Deprecated since common Auditable fields were introduced. Columns and data left untouched to help migration. @@ -98,9 +99,8 @@ public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom { protected ClientTransaction() {} public static ClientTransaction payCharge(final Client client, final Office office, PaymentDetail paymentDetail, - final LocalDate transactionDate, final Money amount, final String currencyCode) { + final LocalDate transactionDate, final Money amount, final String currencyCode, final ExternalId externalId) { final boolean isReversed = false; - final String externalId = null; return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), transactionDate, amount, isReversed, externalId, currencyCode); } @@ -108,14 +108,14 @@ public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom { public static ClientTransaction waiver(final Client client, final Office office, final LocalDate transactionDate, final Money amount, final String currencyCode) { final boolean isReversed = false; - final String externalId = null; + final ExternalId externalId = ExternalId.empty(); final PaymentDetail paymentDetail = null; return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.WAIVE_CHARGE.getValue(), transactionDate, amount, isReversed, externalId, currencyCode); } public ClientTransaction(Client client, Office office, PaymentDetail paymentDetail, Integer typeOf, LocalDate transactionDate, - Money amount, boolean reversed, String externalId, String currencyCode) { + Money amount, boolean reversed, ExternalId externalId, String currencyCode) { this.client = client; this.office = office; @@ -229,4 +229,8 @@ public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom { return this.submittedOnDate; } + public ExternalId getExternalId() { + return this.externalId; + } + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransactionRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransactionRepository.java index f00498c30..5ddcdd49f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransactionRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientTransactionRepository.java @@ -18,9 +18,12 @@ */ package org.apache.fineract.portfolio.client.domain; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface ClientTransactionRepository extends JpaRepository<ClientTransaction, Long>, JpaSpecificationExecutor<ClientTransaction> { + ClientTransaction findByExternalId(ExternalId externalId); + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientNotFoundException.java index 26aad1be5..56e08b4ed 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientNotFoundException.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientNotFoundException.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.client.exception; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; import org.springframework.dao.EmptyResultDataAccessException; @@ -30,6 +31,10 @@ public class ClientNotFoundException extends AbstractPlatformResourceNotFoundExc super("error.msg.client.id.invalid", "Client with identifier " + id + " does not exist", id); } + public ClientNotFoundException(final ExternalId externalId) { + super("error.msg.client.id.invalid", "Client with identifier " + externalId + " does not exist", externalId); + } + public ClientNotFoundException() { super("error.msg.client.not.found.with.basic.details", "Client not found with basic details."); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientTransactionNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientTransactionNotFoundException.java index 0f866d0e5..313b690d7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientTransactionNotFoundException.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/exception/ClientTransactionNotFoundException.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.client.exception; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; import org.springframework.dao.EmptyResultDataAccessException; @@ -29,6 +30,12 @@ public class ClientTransactionNotFoundException extends AbstractPlatformDomainRu clientId); } + public ClientTransactionNotFoundException(final Long clientId, final ExternalId transactionId) { + super("error.msg.client.transaction.not.found.exception", + "The Transaction with id `" + transactionId.getValue() + "` does not exist for a Client with id `" + clientId, + transactionId.getValue(), clientId); + } + public ClientTransactionNotFoundException(Long clientId, Long transactionId, EmptyResultDataAccessException e) { super("error.msg.client.transaction.not.found.exception", "The Transaction with id `" + transactionId + "` does not exist for a Client with id `" + clientId, transactionId, clientId, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java index dadec193c..baa51baa8 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientChargeWritePlatformServiceImpl.java @@ -35,9 +35,11 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; @@ -136,6 +138,8 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final LocalDate transactionDate = command.localDateValueOfParameterNamed(ClientApiConstants.transactionDateParamName); final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName); + final ExternalId transactionExternalId = ExternalIdFactory + .produce(command.stringValueOfParameterNamedAllowingNull(ClientApiConstants.externalIdParamName)); final Money chargePaid = Money.of(clientCharge.getCurrency(), amountPaid); // Validate business rules for payment @@ -149,7 +153,7 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate, - chargePaid, clientCharge.getCurrency().getCode()); + chargePaid, clientCharge.getCurrency().getCode(), transactionExternalId); this.clientTransactionRepository.saveAndFlush(clientTransaction); // update charge paid by associations @@ -162,6 +166,7 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl return new CommandProcessingResultBuilder() // .withTransactionId(clientTransaction.getId().toString())// .withEntityId(clientCharge.getId()) // + .withSubEntityId(clientTransaction.getId()).withSubEntityExternalId(clientTransaction.getExternalId()) .withOfficeId(clientCharge.getClient().getOffice().getId()) // .withClientId(clientCharge.getClient().getId()).build(); } catch (final JpaSystemException | DataIntegrityViolationException dve) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformService.java index 960f97b9c..3eb72cc2c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformService.java @@ -19,9 +19,11 @@ package org.apache.fineract.portfolio.client.service; import java.util.Collection; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.portfolio.client.data.ClientTransactionData; +import org.apache.fineract.portfolio.client.domain.ClientTransaction; import org.springframework.transaction.annotation.Transactional; public interface ClientTransactionReadPlatformService { @@ -35,4 +37,6 @@ public interface ClientTransactionReadPlatformService { @Transactional(readOnly = true) ClientTransactionData retrieveTransaction(Long clientId, Long transactionId); + ClientTransaction retrieveTransactionByExternalId(ExternalId transactionExternalId); + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java index fb2e795c9..1c82138c1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientTransactionReadPlatformServiceImpl.java @@ -25,6 +25,7 @@ import java.time.LocalDate; import java.util.Collection; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.data.EnumOptionData; +import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.PaginationHelper; @@ -33,6 +34,8 @@ import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecific import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.client.data.ClientTransactionData; import org.apache.fineract.portfolio.client.domain.ClientEnumerations; +import org.apache.fineract.portfolio.client.domain.ClientTransaction; +import org.apache.fineract.portfolio.client.domain.ClientTransactionRepository; import org.apache.fineract.portfolio.client.domain.ClientTransactionType; import org.apache.fineract.portfolio.client.exception.ClientTransactionNotFoundException; import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData; @@ -50,6 +53,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti private final DatabaseSpecificSQLGenerator sqlGenerator; private final ClientTransactionMapper clientTransactionMapper = new ClientTransactionMapper(); private final PaginationHelper paginationHelper; + private final ClientTransactionRepository clientTransactionRepository; private static final class ClientTransactionMapper implements RowMapper<ClientTransactionData> { @@ -175,4 +179,9 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti } } + @Override + public ClientTransaction retrieveTransactionByExternalId(ExternalId transactionExternalId) { + return clientTransactionRepository.findByExternalId(transactionExternalId); + } + } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java index 925c5dbce..ff6f34f6d 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/ClientTransactionTest.java @@ -26,6 +26,7 @@ import io.restassured.builder.ResponseSpecBuilder; import io.restassured.http.ContentType; import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; +import java.math.BigDecimal; import java.util.UUID; import org.apache.fineract.client.models.GetClientsClientIdTransactionsResponse; import org.apache.fineract.client.models.GetClientsClientIdTransactionsTransactionIdResponse; @@ -68,6 +69,7 @@ public class ClientTransactionTest { final Integer clientChargeId1 = ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(), ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 October 2011")); Assertions.assertNotNull(clientChargeId1); + String transactionExternalId = UUID.randomUUID().toString(); final String clientChargePaidTransactionId1 = ClientHelper.payChargesForClients(requestSpec, responseSpec, clientId.intValue(), clientChargeId1, ClientHelper.getPayChargeJSON("25 AUGUST 2015", "10")); assertNotNull(clientChargePaidTransactionId1); @@ -75,9 +77,10 @@ public class ClientTransactionTest { final Integer clientChargeId2 = ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(), ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 October 2011")); Assertions.assertNotNull(clientChargeId2); - final String clientChargePaidTransactionId2 = ClientHelper.payChargesForClients(requestSpec, responseSpec, clientId.intValue(), - clientChargeId2, ClientHelper.getPayChargeJSON("25 AUGUST 2015", "10")); - assertNotNull(clientChargePaidTransactionId2); + final String clientChargePaidTransactionExternalId = ClientHelper.payChargesForClientsTransactionExternalId(requestSpec, + responseSpec, clientId.intValue(), clientChargeId2, + ClientHelper.getPayChargeJSONWithExternalId("25 AUGUST 2015", "12", transactionExternalId)); + assertNotNull(clientChargePaidTransactionExternalId); GetClientsClientIdTransactionsResponse allClientTransactionsByExternalId = clientHelper .getAllClientTransactionsByExternalId(clientExternalId); @@ -87,8 +90,17 @@ public class ClientTransactionTest { .getClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId1); assertEquals(Integer.parseInt(clientChargePaidTransactionId1), clientTransactionByExternalId.getId()); + GetClientsClientIdTransactionsTransactionIdResponse clientTransactionByTransactionExternalId = clientHelper + .getClientTransactionByTransactionExternalId(clientId, clientChargePaidTransactionExternalId); + assertNotNull(clientTransactionByTransactionExternalId); + assertEquals(BigDecimal.valueOf(12), clientTransactionByTransactionExternalId.getAmount().stripTrailingZeros()); + PostClientsClientIdTransactionsTransactionIdResponse undoTransactionResponse = clientHelper - .undoClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId2); + .undoClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId1); assertNotNull(undoTransactionResponse.getResourceId()); + + PostClientsClientIdTransactionsTransactionIdResponse undoTransactionResponse2 = clientHelper + .undoClientTransactionByTransactionExternalId(clientId, clientChargePaidTransactionExternalId); + assertNotNull(undoTransactionResponse2.getResourceId()); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java index 7d9fa2cab..4c18dbd71 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ClientHelper.java @@ -638,6 +638,18 @@ public class ClientHelper extends IntegrationTest { return json; } + public static String getPayChargeJSONWithExternalId(final String date, String amount, String externalId) { + final HashMap<String, String> map = new HashMap<>(); + map.put("locale", "en_GB"); + map.put("dateFormat", Utils.DATE_FORMAT); + map.put("transactionDate", date); + map.put("amount", amount); + map.put("externalId", externalId); + String json = GSON.toJson(map); + log.info("{}", json); + return json; + } + public static String getWaiveChargeJSON(final String amount, String clientChargeId) { final HashMap<String, String> map = new HashMap<>(); map.put("locale", "en_GB"); @@ -752,6 +764,15 @@ public class ClientHelper extends IntegrationTest { return response.get("transactionId") != null ? response.get("transactionId").toString() : null; } + public static String payChargesForClientsTransactionExternalId(final RequestSpecification requestSpec, + final ResponseSpecification responseSpec, final Integer clientId, final Integer clientChargeId, final String json) { + log.info("--------------------------------- PAY CHARGES FOR CLIENT --------------------------------"); + final String CHARGES_URL = "/fineract-provider/api/v1/clients/" + clientId + "/charges/" + clientChargeId + "?command=paycharge&" + + Utils.TENANT_IDENTIFIER; + final HashMap<?, ?> response = Utils.performServerPost(requestSpec, responseSpec, CHARGES_URL, json, ""); + return response.get("subResourceExternalId") != null ? response.get("subResourceExternalId").toString() : null; + } + public static String waiveChargesForClients(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer clientId, final Integer clientChargeId, final String json) { log.info("--------------------------------- WAIVE CHARGES FOR CLIENT --------------------------------"); @@ -795,12 +816,22 @@ public class ClientHelper extends IntegrationTest { public GetClientsClientIdTransactionsTransactionIdResponse getClientTransactionByExternalId(final String externalId, final String transactionId) { - return ok(fineract().clientTransactions.retrieveClientTransaction1(externalId, Long.parseLong(transactionId))); + return ok(fineract().clientTransactions.retrieveClientTransaction2(externalId, Long.parseLong(transactionId))); + } + + public GetClientsClientIdTransactionsTransactionIdResponse getClientTransactionByTransactionExternalId(final Long clientId, + final String transactionExternalId) { + return ok(fineract().clientTransactions.retrieveClientTransaction1(clientId, transactionExternalId)); } public PostClientsClientIdTransactionsTransactionIdResponse undoClientTransactionByExternalId(final String externalId, final String transactionId) { - return ok(fineract().clientTransactions.undoClientTransaction1(externalId, Long.parseLong(transactionId), "undo")); + return ok(fineract().clientTransactions.undoClientTransaction2(externalId, Long.parseLong(transactionId), "undo")); + } + + public PostClientsClientIdTransactionsTransactionIdResponse undoClientTransactionByTransactionExternalId(final Long clientId, + final String transactionExternalId) { + return ok(fineract().clientTransactions.undoClientTransaction1(clientId, transactionExternalId, "undo")); } public Workbook getClientEntityWorkbook(GlobalEntityType clientsEntity, String dateFormat) throws IOException {