You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2023/05/25 12:12:59 UTC
[fineract] branch develop updated: [FINERACT-1926] POST API for asset externalization fixes
This is an automated email from the ASF dual-hosted git repository.
adamsaghy 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 105b4ed15 [FINERACT-1926] POST API for asset externalization fixes
105b4ed15 is described below
commit 105b4ed1543ffc619f64ea1e2a3b27a748eabd9a
Author: taskain7 <ta...@gmail.com>
AuthorDate: Tue May 23 07:00:58 2023 +0200
[FINERACT-1926] POST API for asset externalization fixes
---
.../data/ExternalTransferRequestParameters.java | 2 +-
.../ExternalAssetOwnersWriteServiceImpl.java | 42 ++++++++++++---
.../loanaccount/domain/LoanRepository.java | 3 +-
.../loanaccount/domain/LoanRepositoryWrapper.java | 6 ++-
.../service/LoanReadPlatformServiceImpl.java | 5 +-
.../InitiateExternalAssetOwnerTransferTest.java | 60 +++++++++++++++++++++-
6 files changed, 104 insertions(+), 14 deletions(-)
diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java
index 127b58727..dca3343cd 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/data/ExternalTransferRequestParameters.java
@@ -26,6 +26,6 @@ public final class ExternalTransferRequestParameters {
public static final String OWNER_EXTERNAL_ID = "owner_external_id";
public static final String TRANSFER_EXTERNAL_ID = "transfer_external_id";
public static final String PURCHASE_PRICE_RATIO = "purchase_price_ratio";
- public static final String DATEFORMAT = "dateformat";
+ public static final String DATEFORMAT = "dateFormat";
public static final String LOCALE = "locale";
}
diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
index 50876c654..ad54875d1 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
@@ -22,17 +22,22 @@ import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
+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.data.LoanIdAndExternalIdData;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
@@ -64,9 +69,10 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
public CommandProcessingResult saleLoanByLoanId(JsonCommand command) {
Long loanId = command.getLoanId();
LoanIdAndExternalIdData loanIdAndExternalId = loanReadPlatformService.getTransferableLoanIdAndExternalId(loanId);
+ validateLoanStatus(loanIdAndExternalId);
ExternalAssetOwnerTransfer externalAssetOwnerTransfer = parseJson(loanId, command.json(), loanIdAndExternalId.getLoanExternalId(),
ExternalTransferStatus.PENDING);
- validateSale(externalAssetOwnerTransfer, loanIdAndExternalId);
+ validateSale(externalAssetOwnerTransfer);
ExternalAssetOwnerTransfer savedExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository.save(externalAssetOwnerTransfer);
return buildResponseData(savedExternalAssetOwnerTransfer);
}
@@ -76,8 +82,9 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
public CommandProcessingResult buyBackLoanByLoanId(JsonCommand command) {
Long loanId = command.getLoanId();
LoanIdAndExternalIdData loanIdAndExternalId = loanReadPlatformService.getTransferableLoanIdAndExternalId(loanId);
- ExternalAssetOwnerTransfer externalAssetOwnerTransfer = parseJson(command.getLoanId(), command.json(),
- loanIdAndExternalId.getLoanExternalId(), ExternalTransferStatus.BUYBACK);
+ validateLoanStatus(loanIdAndExternalId);
+ ExternalAssetOwnerTransfer externalAssetOwnerTransfer = parseJson(loanId, command.json(), loanIdAndExternalId.getLoanExternalId(),
+ ExternalTransferStatus.BUYBACK);
validateBuyBack(externalAssetOwnerTransfer);
ExternalAssetOwnerTransfer savedExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository.save(externalAssetOwnerTransfer);
return buildResponseData(savedExternalAssetOwnerTransfer);
@@ -99,10 +106,9 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
.with(changes).build();
}
- private void validateSale(ExternalAssetOwnerTransfer externalAssetOwnerTransfer, LoanIdAndExternalIdData loanIdAndExternalId) {
+ private void validateSale(ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
validateSettlementDate(externalAssetOwnerTransfer);
validateTransferStatusForSale(externalAssetOwnerTransfer);
- validateLoanStatus(loanIdAndExternalId);
}
private void validateBuyBack(ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
@@ -117,7 +123,8 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
}
private void validateLoanStatus(LoanIdAndExternalIdData loanIdAndExternalIdAndExternalId) {
- if (Objects.isNull(loanIdAndExternalIdAndExternalId)) {
+ if (Objects.isNull(loanIdAndExternalIdAndExternalId.getLoanId())
+ && Objects.isNull(loanIdAndExternalIdAndExternalId.getLoanExternalId())) {
throw new ExternalAssetOwnerInitiateTransferException("Loan is not in active status");
}
}
@@ -182,6 +189,25 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
ExternalTransferRequestParameters.DATEFORMAT, ExternalTransferRequestParameters.LOCALE));
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, apiRequestBodyAsJson, requestParameters);
+
+ final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+ final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loantransfer");
+ final JsonElement json = fromApiJsonHelper.parse(apiRequestBodyAsJson);
+
+ String ownerExternalId = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.OWNER_EXTERNAL_ID, json);
+ baseDataValidator.reset().parameter(ExternalTransferRequestParameters.OWNER_EXTERNAL_ID).value(ownerExternalId).notBlank()
+ .notExceedingLengthOf(100);
+
+ String transferExternalId = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID, json);
+ baseDataValidator.reset().parameter(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID).value(transferExternalId).ignoreIfNull()
+ .notExceedingLengthOf(100);
+
+ String purchasePriceRatio = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.PURCHASE_PRICE_RATIO, json);
+ baseDataValidator.reset().parameter(ExternalTransferRequestParameters.PURCHASE_PRICE_RATIO).value(purchasePriceRatio).notBlank()
+ .notExceedingLengthOf(50);
+
+ LocalDate settlementDate = fromApiJsonHelper.extractLocalDateNamed(ExternalTransferRequestParameters.SETTLEMENT_DATE, json);
+ baseDataValidator.reset().parameter(ExternalTransferRequestParameters.SETTLEMENT_DATE).value(settlementDate).notNull();
}
private LocalDate getSettlementDateFromJson(JsonElement json) {
@@ -193,7 +219,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
private ExternalId getTransferExternalIdFromJson(JsonElement json) {
String transferExternalId = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID, json);
- return ExternalIdFactory.produce(transferExternalId);
+ return StringUtils.isEmpty(transferExternalId) ? ExternalId.generate() : ExternalIdFactory.produce(transferExternalId);
}
private String getPurchasePriceRatioFromJson(JsonElement json) {
@@ -210,6 +236,6 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
private ExternalAssetOwner createAndGetAssetOwner(String externalId) {
ExternalAssetOwner externalAssetOwner = new ExternalAssetOwner();
externalAssetOwner.setExternalId(ExternalIdFactory.produce(externalId));
- return externalAssetOwnerRepository.save(externalAssetOwner);
+ return externalAssetOwnerRepository.saveAndFlush(externalAssetOwner);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
index 085cc59b7..3f7952d11 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import org.apache.fineract.cob.data.LoanCOBParameter;
import org.apache.fineract.cob.data.LoanIdAndExternalIdAndAccountNo;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
@@ -197,7 +198,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber);
@Query(GET_NON_CLOSED_LOAN_BY_LOAN_ID)
- Loan getNonClosedLoanIdAndExternalIdByLoanId(@Param("loanId") Long loanId);
+ Optional<Loan> getNonClosedLoanIdAndExternalIdByLoanId(@Param("loanId") Long loanId);
@Query(EXISTS_NON_CLOSED_BY_EXTERNAL_LOAN_ID)
boolean existsNonClosedLoanByExternalLoanId(@Param("externalLoanId") ExternalId externalLoanId);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
index f861c3869..f072a5073 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepositoryWrapper.java
@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.ExternalId;
@@ -263,7 +264,10 @@ public class LoanRepositoryWrapper {
return repository.findLoanIdByStatusId(statusId);
}
- public Loan getNonClosedLoanIdAndExternalIdByLoanId(Long loanId) {
+ public Optional<Loan> getNonClosedLoanIdAndExternalIdByLoanId(Long loanId) {
+ if (repository.findById(loanId).isEmpty()) {
+ throw new LoanNotFoundException(loanId);
+ }
return repository.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 60754d1b3..09549592c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -594,8 +594,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
@Override
public LoanIdAndExternalIdData getTransferableLoanIdAndExternalId(Long loanId) {
- Loan loan = loanRepositoryWrapper.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
- return new LoanIdAndExternalIdData(loan.getId(), loan.getExternalId());
+ Optional<Loan> loan = loanRepositoryWrapper.getNonClosedLoanIdAndExternalIdByLoanId(loanId);
+ return loan.map(value -> new LoanIdAndExternalIdData(value.getId(), value.getExternalId()))
+ .orElseGet(() -> new LoanIdAndExternalIdData(null, null));
}
@Override
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
index fe78adeab..f76d727f0 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
@@ -124,6 +124,7 @@ public class InitiateExternalAssetOwnerTransferTest {
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
requestSpec.header("Fineract-Platform-TenantId", "default");
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
@@ -180,6 +181,62 @@ public class InitiateExternalAssetOwnerTransferTest {
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
requestSpec.header("Fineract-Platform-TenantId", "default");
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate);
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+ }
+ }
+
+ @Test
+ public void saleIsNotAllowedWhenLoanIsNotActive() {
+ try {
+ GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
+
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
+ GlobalConfigurationHelper.updateValueForGlobalConfiguration(requestSpec, responseSpec, "10", "0");
+
+ final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
+ Assertions.assertNotNull(clientID);
+
+ Integer overdueFeeChargeId = ChargesHelper.createCharges(requestSpec, responseSpec,
+ ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
+ Assertions.assertNotNull(overdueFeeChargeId);
+
+ final Integer loanProductID = createLoanProduct(overdueFeeChargeId.toString());
+ Assertions.assertNotNull(loanProductID);
+ HashMap loanStatusHashMap;
+
+ final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2020");
+
+ Assertions.assertNotNull(loanID);
+
+ loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+ LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+ loanStatusHashMap = loanTransactionHelper.approveLoan("01 March 2020", loanID);
+ LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+ String loanDetails = loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+ loanStatusHashMap = loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020", loanID,
+ JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+ LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 4));
+
+ loanTransactionHelper.makeRepayment("04 March 2020", 16000.0f, loanID);
+
+ externalAssetOwnerHelper = new ExternalAssetOwnerHelper(requestSpec, responseSpecError);
+ String transferExternalId = "36efeb06-d835-48a1-99eb-09bd1d348c1e";
+ String saleResponse = externalAssetOwnerHelper.initiateTransferByLoanId(loanID.longValue(), "sale",
+ getSaleRequestJson("05 March 2020", transferExternalId));
+ Type type = new TypeToken<Map<String, Object>>() {}.getType();
+ Map<String, Object> errorResponseMap = new Gson().fromJson(saleResponse, type);
+ assertEquals("Loan is not in active status", errorResponseMap.get("developerMessage"));
+ } finally {
+ requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+ requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+ requestSpec.header("Fineract-Platform-TenantId", "default");
+ responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
@@ -244,6 +301,7 @@ public class InitiateExternalAssetOwnerTransferTest {
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
requestSpec.header("Fineract-Platform-TenantId", "default");
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+ BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
}
}
@@ -292,7 +350,7 @@ public class InitiateExternalAssetOwnerTransferTest {
map.put("owner_external_id", "1234567890987654321");
map.put("transfer_external_id", transferExternalId);
map.put("purchase_price_ratio", "1.234");
- map.put("dateformat", "dd MMMM yyyy");
+ map.put("dateFormat", "dd MMMM yyyy");
map.put("locale", "en");
return new Gson().toJson(map);
}