diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java index 5339bf444..1401b2fde 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/SearchParameters.java @@ -47,6 +47,7 @@ public class SearchParameters { private String currencyCode; private Long staffId; private Long loanId; + private Long clientId; private Long savingsId; @Getter(AccessLevel.NONE) private Boolean orphansOnly; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java index 9dc088cc3..6e3a24403 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java @@ -479,6 +479,7 @@ public class LoansApiResource { @QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder, @QueryParam("accountNo") @Parameter(description = "accountNo") final String accountNo, @QueryParam("associations") @Parameter(description = "associations") final String associations, + @QueryParam("clientId") @Parameter(description = "clientId") final Long clientId, @QueryParam("status") @Parameter(description = "status") final String status) { this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS); @@ -488,7 +489,7 @@ public class LoansApiResource { sqlValidator.validate(accountNo); sqlValidator.validate(externalId); final SearchParameters searchParameters = SearchParameters.builder().accountNo(accountNo).sortOrder(sortOrder) - .externalId(externalId).offset(offset).limit(limit).orderBy(orderBy).status(status).build(); + .externalId(externalId).offset(offset).limit(limit).orderBy(orderBy).status(status).clientId(clientId).build(); final Page loanBasicDetails = this.loanReadPlatformService.retrieveAll(searchParameters); final Set associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters()); 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 6cdd34930..908a657a9 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 @@ -359,6 +359,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa arrayPos = arrayPos + 1; } + if (searchParameters.getClientId() != null) { + sqlBuilder.append(" and l.client_id = ?"); + extraCriterias.add(searchParameters.getClientId()); + arrayPos = arrayPos + 1; + } + if (searchParameters.hasOrderBy()) { sqlBuilder.append(" order by ").append(searchParameters.getOrderBy()); this.columnValidator.validateSqlInjection(sqlBuilder.toString(), searchParameters.getOrderBy()); diff --git a/fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm b/fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm index 7edc91a29..ebdf1b92f 100644 --- a/fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm +++ b/fineract-provider/src/main/resources/static/legacy-docs/apiLive.htm @@ -11147,6 +11147,12 @@ GET https://DomainName/api/v1/loans/{loanId}
Use account no. of loans to restrict results.
+
clientId
+
+ String optional +
+
Use client ID of loans to restrict results.
+
associations
String optional, Comma separated list of recurring loan 'associations' (itemized below). 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 13f01d17c..cd0fb38cb 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 @@ -1608,14 +1608,15 @@ public class LoanTransactionHelper { excludedTransactionTypes, page, size, sort)); } - public GetLoansResponse retrieveAllLoans(final String accountNumber, final String associations) { - return Calls.ok( - FineractClientHelper.getFineractClient().loans.retrieveAll27(null, 0, 10, null, null, accountNumber, associations, null)); - } - // TODO: Rewrite to use fineract-client instead! // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long, // org.apache.fineract.client.models.PostLoansLoanIdRequest) + + public GetLoansResponse retrieveAllLoans(final String accountNumber, final String associations, final Long clientId) { + return Calls.ok(FineractClientHelper.getFineractClient().loans.retrieveAll27(null, 0, 10, null, null, accountNumber, associations, + clientId, null)); + } + @Deprecated(forRemoval = true) public GetLoansLoanIdTransactionsTransactionIdResponse getLoanTransaction(final Integer loanId, final Integer txnId) { final String GET_LOAN_CHARGES_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/" + txnId + "?" diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/LoanApiIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/LoanApiIntegrationTest.java index edaad6776..6724346b7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/LoanApiIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/loan/LoanApiIntegrationTest.java @@ -33,6 +33,118 @@ import org.junit.jupiter.api.Test; public class LoanApiIntegrationTest extends BaseLoanIntegrationTest { + @Test + public void test_retrieveLoansByClientId_Works() { + AtomicLong createdLoanId = new AtomicLong(); + AtomicLong createdLoanId2 = new AtomicLong(); + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + Long clientId2 = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + + runAt("01 January 2023", () -> { + // Create Client + + int numberOfRepayments = 3; + int repaymentEvery = 1; + + // Create Loan Products + PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() // + .numberOfRepayments(numberOfRepayments) // + .repaymentEvery(repaymentEvery) // + .installmentAmountInMultiplesOf(null) // + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue()) // + .interestType(InterestType.DECLINING_BALANCE)// + .interestRatePerPeriod(10.0)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// + .interestRecalculationCompoundingMethod(InterestRecalculationCompoundingMethod.NONE)// + .rescheduleStrategyMethod(RescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD)// + .isInterestRecalculationEnabled(true)// + .recalculationRestFrequencyInterval(1)// + .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)// + .rescheduleStrategyMethod(RescheduleStrategyMethod.REDUCE_EMI_AMOUNT)// + .allowPartialPeriodInterestCalcualtion(false)// + .disallowExpectedDisbursements(false)// + .allowApprovedDisbursedAmountsOverApplied(false)// + .overAppliedNumber(null)// + .overAppliedCalculationType(null)// + .multiDisburseLoan(null);// + + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); + Long loanProductId = loanProductResponse.getResourceId(); + + PostLoanProductsRequest product2 = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() // + .numberOfRepayments(numberOfRepayments) // + .repaymentEvery(repaymentEvery) // + .installmentAmountInMultiplesOf(null) // + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue()) // + .interestType(InterestType.DECLINING_BALANCE)// + .interestRatePerPeriod(10.0)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// + .interestRecalculationCompoundingMethod(InterestRecalculationCompoundingMethod.NONE)// + .rescheduleStrategyMethod(RescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD)// + .isInterestRecalculationEnabled(true)// + .recalculationRestFrequencyInterval(1)// + .recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY)// + .rescheduleStrategyMethod(RescheduleStrategyMethod.REDUCE_EMI_AMOUNT)// + .allowPartialPeriodInterestCalcualtion(false)// + .disallowExpectedDisbursements(false)// + .allowApprovedDisbursedAmountsOverApplied(false)// + .overAppliedNumber(null)// + .overAppliedCalculationType(null)// + .multiDisburseLoan(null);// + + PostLoanProductsResponse loanProductResponse2 = loanProductHelper.createLoanProduct(product2); + Long loanProductId2 = loanProductResponse2.getResourceId(); + + // Apply and Approve Loan + double amount = 5000.0; + + PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2023", amount, numberOfRepayments)// + .repaymentEvery(repaymentEvery)// + .interestRatePerPeriod(BigDecimal.valueOf(10.0))// + .loanTermFrequency(numberOfRepayments)// + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS)// + .loanTermFrequencyType(RepaymentFrequencyType.MONTHS)// + .interestType(InterestType.DECLINING_BALANCE)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY);// + + PostLoansRequest applicationRequest2 = applyLoanRequest(clientId2, loanProductId2, "01 January 2023", amount, + numberOfRepayments)// + .repaymentEvery(repaymentEvery)// + .interestRatePerPeriod(BigDecimal.valueOf(10.0))// + .loanTermFrequency(numberOfRepayments)// + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS)// + .loanTermFrequencyType(RepaymentFrequencyType.MONTHS)// + .interestType(InterestType.DECLINING_BALANCE)// + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY);// + + PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest); + PostLoansResponse postLoansResponse2 = loanTransactionHelper.applyLoan(applicationRequest2); + + PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), + approveLoanRequest(amount, "01 January 2023")); + + PostLoansLoanIdResponse approvedLoanResult2 = loanTransactionHelper.approveLoan(postLoansResponse2.getResourceId(), + approveLoanRequest(amount, "01 January 2023")); + + Long loanId = approvedLoanResult.getLoanId(); + Long loanId2 = approvedLoanResult2.getLoanId(); + createdLoanId.getAndSet(loanId); + createdLoanId2.getAndSet(loanId2); + + // disburse Loan + disburseLoan(loanId, BigDecimal.valueOf(amount), "01 January 2023"); + disburseLoan(loanId2, BigDecimal.valueOf(amount), "01 January 2023"); + }); + runAt("01 February 2023", () -> { + long loanId = createdLoanId.get(); + GetLoansResponse loansLoanIdResponse = loanTransactionHelper.retrieveAllLoans(null, null, clientId); + assertThat(loansLoanIdResponse.getPageItems()).isNotNull(); + assertThat(loansLoanIdResponse.getPageItems().size()).isEqualTo(1); + Long loanIdFromResponse = loansLoanIdResponse.getPageItems().iterator().next().getId(); + assertThat(loanIdFromResponse).isEqualTo(loanId); + }); + } + @Test public void test_retrieveLoansWithSummary_Works() { AtomicLong createdLoanId = new AtomicLong(); @@ -95,7 +207,7 @@ public class LoanApiIntegrationTest extends BaseLoanIntegrationTest { runAt("01 February 2023", () -> { long loanId = createdLoanId.get(); GetLoansLoanIdResponse loanResponse = loanTransactionHelper.getLoanDetails(loanId); - GetLoansResponse loansLoanIdResponse = loanTransactionHelper.retrieveAllLoans(loanResponse.getAccountNo(), "summary"); + GetLoansResponse loansLoanIdResponse = loanTransactionHelper.retrieveAllLoans(loanResponse.getAccountNo(), "summary", null); BigDecimal totalUnpaidPayableDueInterest = loansLoanIdResponse.getPageItems().iterator().next().getSummary() .getTotalUnpaidPayableDueInterest(); assertThat(totalUnpaidPayableDueInterest).isEqualByComparingTo(BigDecimal.valueOf(509.59));