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 16c8216cd..44dd7e756 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 @@ -161,4 +161,6 @@ public interface LoanRepository extends JpaRepository, JpaSpecificat @Query(FIND_BY_ACCOUNT_NUMBER) Loan findLoanAccountByAccountNumber(@Param("accountNumber") String accountNumber); + boolean existsByExternalId(@Param("externalId") String externalId); + } 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 d7bb7adef..5ad13c870 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 @@ -240,6 +240,10 @@ public class LoanRepositoryWrapper { return this.repository.findNonClosedLoanByAccountNumber(accountNumber); } + public boolean existLoanByExternalId(final String externalId) { + return this.repository.existsByExternalId(externalId); + } + // Looks like we need complete entity @Transactional(readOnly = true) public Loan findNonClosedLoanThatBelongsToClient(@Param("loanId") Long loanId, @Param("clientId") Long clientId) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 368e45bcb..606077564 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -303,6 +303,16 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa this.fromApiJsonDeserializer.validateForCreate(command.json(), isMeetingMandatoryForJLGLoans, loanProduct); + // Validate If the externalId is already registered + final String externalId = this.fromJsonHelper.extractStringNamed("externalId", command.parsedJson()); + if (StringUtils.isNotBlank(externalId)) { + final boolean existByExternalId = this.loanRepositoryWrapper.existLoanByExternalId(externalId); + if (existByExternalId) { + throw new GeneralPlatformDomainRuleException("error.msg.loan.with.externalId.already.used", + "Loan with externalId is already registered."); + } + } + final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan"); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java index 690dfe88a..6c52a8d13 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java @@ -19,6 +19,7 @@ package org.apache.fineract.integrationtests; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -125,6 +126,26 @@ public class ClientLoanIntegrationTest { verifyLoanRepaymentSchedule(loanSchedule); } + @Test + public void validateClientLoanWithUniqueExternalId() { + // Given + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + final ResponseSpecification responseSpec403 = new ResponseSpecBuilder().expectStatusCode(403).build(); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + final Integer loanProductID = createLoanProduct(false, NONE); + + final String externalId = Utils.randomStringGenerator("qwerty", 10); + + // When + final Integer loanID = applyForLoanApplicationWithExternalId(this.requestSpec, this.responseSpec, clientID, loanProductID, + "12,000.00", externalId); + + // Then + assertNotNull(loanID); + applyForLoanApplicationWithExternalId(this.requestSpec, responseSpec403, clientID, loanProductID, "12,000.00", externalId); + } + @Test public void testLoanCharges_DISBURSEMENT_FEE() { this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); @@ -1155,6 +1176,27 @@ public class ClientLoanIntegrationTest { return this.loanTransactionHelper.getLoanId(loanApplicationJSON); } + private Integer applyForLoanApplicationWithExternalId(RequestSpecification requestSpec, ResponseSpecification responseSpec, + final Integer clientID, final Integer loanProductID, String principal, final String externalId) { + LOG.info("------------------------APPLYING FOR LOAN APPLICATION WITH EXTERNALID------------------------"); + final String loanApplicationJSON = new LoanApplicationTestBuilder() // + .withPrincipal(principal) // + .withExternalId(externalId) // + .withLoanTermFrequency("4") // + .withLoanTermFrequencyAsMonths() // + .withNumberOfRepayments("4") // + .withRepaymentEveryAfter("1") // + .withRepaymentFrequencyTypeAsMonths() // + .withInterestRatePerPeriod("2") // + .withAmortizationTypeAsEqualInstallments() // + .withInterestTypeAsDecliningBalance() // + .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() // + .withExpectedDisbursementDate("20 September 2011") // + .withSubmittedOnDate("20 September 2011") // + .build(clientID.toString(), loanProductID.toString(), null); + return this.loanTransactionHelper.getLoanId(loanApplicationJSON, requestSpec, responseSpec); + } + private Integer applyForLoanApplicationWithTranches(final Integer clientID, final Integer loanProductID, List charges, final String savingsId, String principal, List tranches, List collaterals) { LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java index 76f0664a8..a480de000 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanApplicationTestBuilder.java @@ -43,6 +43,7 @@ public class LoanApplicationTestBuilder { public static final String RBI_INDIA_STRATEGY = "4"; public static final String INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY = "6"; + private String externalId = null; private String principal = "10,000"; private String glimPrincipal = "1000"; private String loanTermFrequency = ""; @@ -124,7 +125,7 @@ public class LoanApplicationTestBuilder { if (this.glimPrincipal != null) { map.put("glimPrincipal", this.glimPrincipal); } - map.put("locale", "en_GB"); + map.put("locale", LOCALE); String approvalFormData = new Gson().toJson(map); LOG.info("approvalFormData: {} ", approvalFormData); @@ -153,6 +154,10 @@ public class LoanApplicationTestBuilder { map.put("collateral", this.collaterals); map.put("interestChargedFromDate", this.interestChargedFromDate); + if (this.externalId != null) { + map.put("externalId", this.externalId); + } + if (repaymentsStartingFromDate != null) { map.put("repaymentsStartingFromDate", this.repaymentsStartingFromDate); } @@ -185,6 +190,11 @@ public class LoanApplicationTestBuilder { return new Gson().toJson(map); } + public LoanApplicationTestBuilder withExternalId(final String externalId) { + this.externalId = externalId; + return this; + } + public LoanApplicationTestBuilder withPrincipal(final String principalAmount) { this.principal = principalAmount; return this; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index e2688f2da..b0a0121ed 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -81,7 +81,11 @@ public class LoanTransactionHelper { } public Integer getLoanId(final String loanApplicationJSON) { - return Utils.performServerPost(this.requestSpec, this.responseSpec, APPLY_LOAN_URL, loanApplicationJSON, "loanId"); + return this.getLoanId(loanApplicationJSON, this.requestSpec, this.responseSpec); + } + + public Integer getLoanId(final String loanApplicationJSON, RequestSpecification requestSpec, ResponseSpecification responseSpec) { + return Utils.performServerPost(requestSpec, responseSpec, APPLY_LOAN_URL, loanApplicationJSON, "loanId"); } public HashMap getGlimId(final String loanApplicationJSON) {