[FINERACT-1760] Client transaction by transaction external ID

This commit is contained in:
taskain7
2023-07-24 08:27:07 +02:00
committed by Arnold Gálovics
parent a439952476
commit 8fa1c4571a
12 changed files with 229 additions and 24 deletions
@@ -162,6 +162,8 @@ final class ClientChargesApiResourceSwagger {
public String dateFormat; public String dateFormat;
@Schema(example = "01 September 2015") @Schema(example = "01 September 2015")
public String transactionDate; public String transactionDate;
@Schema(example = "3e7791ce-aa10-11ec-b909-0242ac120002")
public String externalId;
} }
@Schema(description = "PostClientsClientIdChargesChargeIdResponse") @Schema(description = "PostClientsClientIdChargesChargeIdResponse")
@@ -43,6 +43,7 @@ import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService; import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper; import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; 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.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; 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.core.service.SearchParameters;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.client.data.ClientTransactionData; 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.ClientNotFoundException;
import org.apache.fineract.portfolio.client.exception.ClientTransactionNotFoundException;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
import org.apache.fineract.portfolio.client.service.ClientTransactionReadPlatformService; import org.apache.fineract.portfolio.client.service.ClientTransactionReadPlatformService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -122,7 +125,8 @@ public class ClientTransactionsApiResource {
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Client Transactions", description = "The list capability of client transaction can support pagination." @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({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsResponse.class))) })
public String retrieveAllClientTransactions( public String retrieveAllClientTransactions(
@@ -131,9 +135,11 @@ public class ClientTransactionsApiResource {
@QueryParam("limit") @Parameter(description = "limit") final Integer limit) { @QueryParam("limit") @Parameter(description = "limit") final Integer limit) {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); 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)) { if (Objects.isNull(clientId)) {
throw new ClientNotFoundException(); throw new ClientNotFoundException(clientExtId);
} }
return getAllClientTransactions(clientId, uriInfo, offset, limit); return getAllClientTransactions(clientId, uriInfo, offset, limit);
@@ -143,8 +149,9 @@ public class ClientTransactionsApiResource {
@Path("external-id/{clientExternalId}/transactions/{transactionId}") @Path("external-id/{clientExternalId}/transactions/{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a Client Transaction", description = "Example Requests:\n" + "clients/1/transactions/1\n" + "\n" + "\n" @Operation(summary = "Retrieve a Client Transaction", description = "Example Requests:\n"
+ "clients/1/transactions/1?fields=id,officeName") + "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({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsTransactionIdResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ClientTransactionsApiResourceSwagger.GetClientsClientIdTransactionsTransactionIdResponse.class))) })
public String retrieveClientTransaction( public String retrieveClientTransaction(
@@ -154,10 +161,11 @@ public class ClientTransactionsApiResource {
context.authenticatedUser().validateHasReadPermission(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); 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)) { if (Objects.isNull(clientId)) {
throw new ClientNotFoundException(); throw new ClientNotFoundException(clientExtId);
} }
return getClientTransaction(clientId, transactionId, uriInfo); return getClientTransaction(clientId, transactionId, uriInfo);
@@ -176,9 +184,119 @@ public class ClientTransactionsApiResource {
@QueryParam("command") @Parameter(description = "command") final String commandParam, @QueryParam("command") @Parameter(description = "command") final String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) { @Parameter(hidden = true) final String apiRequestBodyAsJson) {
Long clientId = resolveClientId(clientExternalId); ExternalId clientExtId = ExternalIdFactory.produce(clientExternalId);
Long clientId = resolveClientId(clientExtId);
if (Objects.isNull(clientId)) { if (Objects.isNull(clientId)) {
throw new ClientNotFoundException(); 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(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); return undoTransaction(clientId, transactionId, commandParam, apiRequestBodyAsJson);
@@ -214,8 +332,13 @@ public class ClientTransactionsApiResource {
} }
} }
private Long resolveClientId(String clientExternalId) { private Long resolveClientId(ExternalId clientExternalId) {
return clientReadPlatformService.retrieveClientIdByExternalId(ExternalIdFactory.produce(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) { private boolean is(final String commandParam, final String commandValue) {
@@ -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, protected static final Set<String> CLIENT_CHARGES_PAY_CHARGE_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(amountParamName,
transactionDateParamName, dateFormatParamName, localeParamName, paymentTypeIdParamName, transactionAccountNumberParamName, transactionDateParamName, dateFormatParamName, localeParamName, paymentTypeIdParamName, transactionAccountNumberParamName,
checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName)); checkNumberParamName, routingCodeParamName, receiptNumberParamName, bankNumberParamName, externalIdParamName));
} }
@@ -39,6 +39,7 @@ import java.util.Set;
import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom; 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.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
@@ -81,7 +82,7 @@ public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom {
private boolean reversed; private boolean reversed;
@Column(name = "external_id", length = 100, nullable = true, unique = true) @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. * 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() {} protected ClientTransaction() {}
public static ClientTransaction payCharge(final Client client, final Office office, PaymentDetail paymentDetail, 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 boolean isReversed = false;
final String externalId = null;
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), transactionDate, amount, return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.PAY_CHARGE.getValue(), transactionDate, amount,
isReversed, externalId, currencyCode); 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, public static ClientTransaction waiver(final Client client, final Office office, final LocalDate transactionDate, final Money amount,
final String currencyCode) { final String currencyCode) {
final boolean isReversed = false; final boolean isReversed = false;
final String externalId = null; final ExternalId externalId = ExternalId.empty();
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.WAIVE_CHARGE.getValue(), transactionDate, amount, return new ClientTransaction(client, office, paymentDetail, ClientTransactionType.WAIVE_CHARGE.getValue(), transactionDate, amount,
isReversed, externalId, currencyCode); isReversed, externalId, currencyCode);
} }
public ClientTransaction(Client client, Office office, PaymentDetail paymentDetail, Integer typeOf, LocalDate transactionDate, 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.client = client;
this.office = office; this.office = office;
@@ -229,4 +229,8 @@ public class ClientTransaction extends AbstractAuditableWithUTCDateTimeCustom {
return this.submittedOnDate; return this.submittedOnDate;
} }
public ExternalId getExternalId() {
return this.externalId;
}
} }
@@ -18,9 +18,12 @@
*/ */
package org.apache.fineract.portfolio.client.domain; 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.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ClientTransactionRepository extends JpaRepository<ClientTransaction, Long>, JpaSpecificationExecutor<ClientTransaction> { public interface ClientTransactionRepository extends JpaRepository<ClientTransaction, Long>, JpaSpecificationExecutor<ClientTransaction> {
ClientTransaction findByExternalId(ExternalId externalId);
} }
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.portfolio.client.exception; 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.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException; 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); 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() { public ClientNotFoundException() {
super("error.msg.client.not.found.with.basic.details", "Client not found with basic details."); super("error.msg.client.not.found.with.basic.details", "Client not found with basic details.");
} }
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.portfolio.client.exception; 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.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
@@ -29,6 +30,12 @@ public class ClientTransactionNotFoundException extends AbstractPlatformDomainRu
clientId); 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) { public ClientTransactionNotFoundException(Long clientId, Long transactionId, EmptyResultDataAccessException e) {
super("error.msg.client.transaction.not.found.exception", super("error.msg.client.transaction.not.found.exception",
"The Transaction with id `" + transactionId + "` does not exist for a Client with id `" + clientId, transactionId, clientId, "The Transaction with id `" + transactionId + "` does not exist for a Client with id `" + clientId, transactionId, clientId,
@@ -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.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; 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.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.DateUtils; 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.holiday.domain.HolidayRepositoryWrapper;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; 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 DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
final LocalDate transactionDate = command.localDateValueOfParameterNamed(ClientApiConstants.transactionDateParamName); final LocalDate transactionDate = command.localDateValueOfParameterNamed(ClientApiConstants.transactionDateParamName);
final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName); final BigDecimal amountPaid = command.bigDecimalValueOfParameterNamed(ClientApiConstants.amountParamName);
final ExternalId transactionExternalId = ExternalIdFactory
.produce(command.stringValueOfParameterNamedAllowingNull(ClientApiConstants.externalIdParamName));
final Money chargePaid = Money.of(clientCharge.getCurrency(), amountPaid); final Money chargePaid = Money.of(clientCharge.getCurrency(), amountPaid);
// Validate business rules for payment // Validate business rules for payment
@@ -149,7 +153,7 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl
final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate, ClientTransaction clientTransaction = ClientTransaction.payCharge(client, client.getOffice(), paymentDetail, transactionDate,
chargePaid, clientCharge.getCurrency().getCode()); chargePaid, clientCharge.getCurrency().getCode(), transactionExternalId);
this.clientTransactionRepository.saveAndFlush(clientTransaction); this.clientTransactionRepository.saveAndFlush(clientTransaction);
// update charge paid by associations // update charge paid by associations
@@ -162,6 +166,7 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
.withTransactionId(clientTransaction.getId().toString())// .withTransactionId(clientTransaction.getId().toString())//
.withEntityId(clientCharge.getId()) // .withEntityId(clientCharge.getId()) //
.withSubEntityId(clientTransaction.getId()).withSubEntityExternalId(clientTransaction.getExternalId())
.withOfficeId(clientCharge.getClient().getOffice().getId()) // .withOfficeId(clientCharge.getClient().getOffice().getId()) //
.withClientId(clientCharge.getClient().getId()).build(); .withClientId(clientCharge.getClient().getId()).build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) { } catch (final JpaSystemException | DataIntegrityViolationException dve) {
@@ -19,9 +19,11 @@
package org.apache.fineract.portfolio.client.service; package org.apache.fineract.portfolio.client.service;
import java.util.Collection; 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.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.portfolio.client.data.ClientTransactionData; import org.apache.fineract.portfolio.client.data.ClientTransactionData;
import org.apache.fineract.portfolio.client.domain.ClientTransaction;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
public interface ClientTransactionReadPlatformService { public interface ClientTransactionReadPlatformService {
@@ -35,4 +37,6 @@ public interface ClientTransactionReadPlatformService {
@Transactional(readOnly = true) @Transactional(readOnly = true)
ClientTransactionData retrieveTransaction(Long clientId, Long transactionId); ClientTransactionData retrieveTransaction(Long clientId, Long transactionId);
ClientTransaction retrieveTransactionByExternalId(ExternalId transactionExternalId);
} }
@@ -25,6 +25,7 @@ import java.time.LocalDate;
import java.util.Collection; import java.util.Collection;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; 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.domain.JdbcSupport;
import org.apache.fineract.infrastructure.core.service.Page; import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.PaginationHelper; 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.organisation.monetary.data.CurrencyData;
import org.apache.fineract.portfolio.client.data.ClientTransactionData; import org.apache.fineract.portfolio.client.data.ClientTransactionData;
import org.apache.fineract.portfolio.client.domain.ClientEnumerations; 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.domain.ClientTransactionType;
import org.apache.fineract.portfolio.client.exception.ClientTransactionNotFoundException; import org.apache.fineract.portfolio.client.exception.ClientTransactionNotFoundException;
import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData; import org.apache.fineract.portfolio.paymentdetail.data.PaymentDetailData;
@@ -50,6 +53,7 @@ public class ClientTransactionReadPlatformServiceImpl implements ClientTransacti
private final DatabaseSpecificSQLGenerator sqlGenerator; private final DatabaseSpecificSQLGenerator sqlGenerator;
private final ClientTransactionMapper clientTransactionMapper = new ClientTransactionMapper(); private final ClientTransactionMapper clientTransactionMapper = new ClientTransactionMapper();
private final PaginationHelper paginationHelper; private final PaginationHelper paginationHelper;
private final ClientTransactionRepository clientTransactionRepository;
private static final class ClientTransactionMapper implements RowMapper<ClientTransactionData> { 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);
}
} }
@@ -26,6 +26,7 @@ import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType; import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal;
import java.util.UUID; import java.util.UUID;
import org.apache.fineract.client.models.GetClientsClientIdTransactionsResponse; import org.apache.fineract.client.models.GetClientsClientIdTransactionsResponse;
import org.apache.fineract.client.models.GetClientsClientIdTransactionsTransactionIdResponse; import org.apache.fineract.client.models.GetClientsClientIdTransactionsTransactionIdResponse;
@@ -68,6 +69,7 @@ public class ClientTransactionTest {
final Integer clientChargeId1 = ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(), final Integer clientChargeId1 = ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(),
ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 October 2011")); ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 October 2011"));
Assertions.assertNotNull(clientChargeId1); Assertions.assertNotNull(clientChargeId1);
String transactionExternalId = UUID.randomUUID().toString();
final String clientChargePaidTransactionId1 = ClientHelper.payChargesForClients(requestSpec, responseSpec, clientId.intValue(), final String clientChargePaidTransactionId1 = ClientHelper.payChargesForClients(requestSpec, responseSpec, clientId.intValue(),
clientChargeId1, ClientHelper.getPayChargeJSON("25 AUGUST 2015", "10")); clientChargeId1, ClientHelper.getPayChargeJSON("25 AUGUST 2015", "10"));
assertNotNull(clientChargePaidTransactionId1); assertNotNull(clientChargePaidTransactionId1);
@@ -75,9 +77,10 @@ public class ClientTransactionTest {
final Integer clientChargeId2 = ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(), final Integer clientChargeId2 = ClientHelper.addChargesForClient(requestSpec, responseSpec, clientId.intValue(),
ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 October 2011")); ClientHelper.getSpecifiedDueDateChargesClientAsJSON(chargeId.toString(), "29 October 2011"));
Assertions.assertNotNull(clientChargeId2); Assertions.assertNotNull(clientChargeId2);
final String clientChargePaidTransactionId2 = ClientHelper.payChargesForClients(requestSpec, responseSpec, clientId.intValue(), final String clientChargePaidTransactionExternalId = ClientHelper.payChargesForClientsTransactionExternalId(requestSpec,
clientChargeId2, ClientHelper.getPayChargeJSON("25 AUGUST 2015", "10")); responseSpec, clientId.intValue(), clientChargeId2,
assertNotNull(clientChargePaidTransactionId2); ClientHelper.getPayChargeJSONWithExternalId("25 AUGUST 2015", "12", transactionExternalId));
assertNotNull(clientChargePaidTransactionExternalId);
GetClientsClientIdTransactionsResponse allClientTransactionsByExternalId = clientHelper GetClientsClientIdTransactionsResponse allClientTransactionsByExternalId = clientHelper
.getAllClientTransactionsByExternalId(clientExternalId); .getAllClientTransactionsByExternalId(clientExternalId);
@@ -87,8 +90,17 @@ public class ClientTransactionTest {
.getClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId1); .getClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId1);
assertEquals(Integer.parseInt(clientChargePaidTransactionId1), clientTransactionByExternalId.getId()); assertEquals(Integer.parseInt(clientChargePaidTransactionId1), clientTransactionByExternalId.getId());
GetClientsClientIdTransactionsTransactionIdResponse clientTransactionByTransactionExternalId = clientHelper
.getClientTransactionByTransactionExternalId(clientId, clientChargePaidTransactionExternalId);
assertNotNull(clientTransactionByTransactionExternalId);
assertEquals(BigDecimal.valueOf(12), clientTransactionByTransactionExternalId.getAmount().stripTrailingZeros());
PostClientsClientIdTransactionsTransactionIdResponse undoTransactionResponse = clientHelper PostClientsClientIdTransactionsTransactionIdResponse undoTransactionResponse = clientHelper
.undoClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId2); .undoClientTransactionByExternalId(clientExternalId, clientChargePaidTransactionId1);
assertNotNull(undoTransactionResponse.getResourceId()); assertNotNull(undoTransactionResponse.getResourceId());
PostClientsClientIdTransactionsTransactionIdResponse undoTransactionResponse2 = clientHelper
.undoClientTransactionByTransactionExternalId(clientId, clientChargePaidTransactionExternalId);
assertNotNull(undoTransactionResponse2.getResourceId());
} }
} }
@@ -638,6 +638,18 @@ public class ClientHelper extends IntegrationTest {
return json; 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) { public static String getWaiveChargeJSON(final String amount, String clientChargeId) {
final HashMap<String, String> map = new HashMap<>(); final HashMap<String, String> map = new HashMap<>();
map.put("locale", "en_GB"); map.put("locale", "en_GB");
@@ -752,6 +764,15 @@ public class ClientHelper extends IntegrationTest {
return response.get("transactionId") != null ? response.get("transactionId").toString() : null; 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, public static String waiveChargesForClients(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final Integer clientId, final Integer clientChargeId, final String json) { final Integer clientId, final Integer clientChargeId, final String json) {
log.info("--------------------------------- WAIVE CHARGES FOR CLIENT --------------------------------"); log.info("--------------------------------- WAIVE CHARGES FOR CLIENT --------------------------------");
@@ -795,12 +816,22 @@ public class ClientHelper extends IntegrationTest {
public GetClientsClientIdTransactionsTransactionIdResponse getClientTransactionByExternalId(final String externalId, public GetClientsClientIdTransactionsTransactionIdResponse getClientTransactionByExternalId(final String externalId,
final String transactionId) { 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, public PostClientsClientIdTransactionsTransactionIdResponse undoClientTransactionByExternalId(final String externalId,
final String transactionId) { 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 { public Workbook getClientEntityWorkbook(GlobalEntityType clientsEntity, String dateFormat) throws IOException {