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 <[email protected]>
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);
     }

Reply via email to