diff --git a/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java b/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java index e93b656f2..bad8b26cb 100644 --- a/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java +++ b/fineract-core/src/main/java/org/apache/fineract/infrastructure/dataqueries/api/DataTableApiConstant.java @@ -54,6 +54,7 @@ public final class DataTableApiConstant { public static final String meetingAssociateParamName = "meeting"; public static final String emiAmountVariationsAssociateParamName = "emiAmountVariations"; public static final String collectionAssociateParamName = "collection"; + public static final String originatorsAssociateParamName = "originators"; public static final String TABLE_COLUMN_CODE_MAPPINGS = "x_table_column_code_mappings"; public static final String TABLE_REGISTERED_TABLE = "x_registered_table"; diff --git a/fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/data/LoanOriginatorData.java b/fineract-core/src/main/java/org/apache/fineract/portfolio/loanorigination/data/LoanOriginatorData.java similarity index 100% rename from fineract-loan-origination/src/main/java/org/apache/fineract/portfolio/loanorigination/data/LoanOriginatorData.java rename to fineract-core/src/main/java/org/apache/fineract/portfolio/loanorigination/data/LoanOriginatorData.java diff --git a/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0003_mapping_permissions.xml b/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0003_mapping_permissions.xml index 3b750c200..c302abadb 100644 --- a/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0003_mapping_permissions.xml +++ b/fineract-loan-origination/src/main/resources/db/changelog/tenant/module/loanorigination/parts/0003_mapping_permissions.xml @@ -1,5 +1,6 @@ notes; private Collection disbursementDetails; private LoanScheduleData originalSchedule; + private Collection originators; // template private Collection productOptions; private Collection loanOfficerOptions; 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 c7c3c0bdc..bf68bede5 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 @@ -57,6 +57,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import lombok.RequiredArgsConstructor; import org.apache.fineract.commands.domain.CommandWrapper; @@ -165,6 +166,8 @@ import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformS import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService; import org.apache.fineract.portfolio.loanaccount.service.LoanSummaryDataProvider; import org.apache.fineract.portfolio.loanaccount.service.LoanSummaryProviderDelegate; +import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData; +import org.apache.fineract.portfolio.loanorigination.service.LoanOriginatorReadPlatformService; import org.apache.fineract.portfolio.loanproduct.LoanProductConstants; import org.apache.fineract.portfolio.loanproduct.data.LoanProductData; import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData; @@ -255,9 +258,9 @@ public class LoansApiResource { "repaymentFrequencyNthDayTypeOptions", "repaymentFrequencyDaysOfWeekTypeOptions", "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting", - "loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount", "interestRateDifferential", - "isFloatingInterestRate", "interestRatesPeriods", "lastClosedBusinessDate", LoanApiConstants.canUseForTopup, - LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, + "loanCounter", "loanProductCounter", "notes", "originators", "accountLinkingOptions", "linkedAccount", + "interestRateDifferential", "isFloatingInterestRate", "interestRatesPeriods", "lastClosedBusinessDate", + LoanApiConstants.canUseForTopup, LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, LoanApiConstants.clientActiveLoanOptions, LoanApiConstants.datatables, LoanProductConstants.RATES_PARAM_NAME, LoanApiConstants.MULTIDISBURSE_DETAILS_PARAMNAME, LoanApiConstants.EMI_AMOUNT_VARIATIONS_PARAMNAME, LoanApiConstants.COLLECTION_PARAMNAME, LoanApiConstants.INTEREST_RECOGNITION_ON_DISBURSEMENT_DATE, @@ -310,6 +313,7 @@ public class LoansApiResource { private final LoanSummaryProviderDelegate loanSummaryProviderDelegate; private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository; private final LoanApprovedAmountHistoryRepository loanApprovedAmountHistoryRepository; + private final Optional loanOriginatorReadPlatformService; /* * This template API is used for loan approval, ideally this should be invoked on loan that are pending for @@ -1052,7 +1056,7 @@ public class LoansApiResource { DataTableApiConstant.guarantorsAssociateParamName, DataTableApiConstant.collateralAssociateParamName, DataTableApiConstant.notesAssociateParamName, DataTableApiConstant.linkedAccountAssociateParamName, DataTableApiConstant.multiDisburseDetailsAssociateParamName, DataTableApiConstant.collectionAssociateParamName, - DataTableApiConstant.loanTermVariationsAssociateParamName)); + DataTableApiConstant.loanTermVariationsAssociateParamName, DataTableApiConstant.originatorsAssociateParamName)); } ApiParameterHelper.excludeAssociationsForResponseIfProvided(exclude, associationParameters); @@ -1158,6 +1162,14 @@ public class LoansApiResource { mandatoryResponseParameters.add(DataTableApiConstant.linkedAccountAssociateParamName); linkedAccount = this.accountAssociationsReadPlatformService.retriveLoanLinkedAssociation(resolvedLoanId); } + + if (associationParameters.contains(DataTableApiConstant.originatorsAssociateParamName)) { + mandatoryResponseParameters.add(DataTableApiConstant.originatorsAssociateParamName); + if (loanOriginatorReadPlatformService.isPresent()) { + List originatorList = loanOriginatorReadPlatformService.get().retrieveByLoanId(resolvedLoanId); + loanBasicDetails.setOriginators(originatorList.isEmpty() ? Collections.emptyList() : originatorList); + } + } } Collection productOptions = null; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java index 80a147e13..f00089189 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers/FeignLoanHelper.java @@ -90,6 +90,14 @@ public class FeignLoanHelper { return ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "all", "exclude", "guarantors,futureSchedule"))); } + public GetLoansLoanIdResponse getLoanDetailsWithAssociations(Long loanId, String associations) { + return ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", associations))); + } + + public GetLoansLoanIdResponse getLoanDetailsWithAssociationsAndExclude(Long loanId, String associations, String exclude) { + return ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", associations, "exclude", exclude))); + } + public void undoApproval(Long loanId) { PostLoansLoanIdRequest request = new PostLoansLoanIdRequest(); ok(() -> fineractClient.loans().stateTransitions(loanId, request, Map.of("command", "undoApproval"))); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java index dcba22dcb..cf9138032 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/tests/FeignLoanOriginatorApiTest.java @@ -343,4 +343,74 @@ public class FeignLoanOriginatorApiTest extends FeignIntegrationTest { originatorHelper.deleteOriginator(originatorId); } + + @Test + public void testRetrieveLoanWithOriginatorsAssociation() { + final String originatorExternalId = FeignLoanOriginatorHelper.generateUniqueExternalId(); + final Long originatorId = originatorHelper.createOriginator(originatorExternalId); + + final Long clientId = clientHelper.createClient("Test", "Client"); + final Long loanId = loanHelper.createSubmittedLoan(clientId); + + originatorHelper.attachOriginatorToLoan(loanId, originatorId); + + final var loanDetails = loanHelper.getLoanDetailsWithAssociations(loanId, "originators"); + + assertThat(loanDetails.getOriginators()).isNotNull(); + assertThat(loanDetails.getOriginators()).hasSize(1); + assertThat(loanDetails.getOriginators().get(0).getId()).isEqualTo(originatorId); + assertThat(loanDetails.getOriginators().get(0).getExternalId()).isEqualTo(originatorExternalId); + + originatorHelper.detachOriginatorFromLoan(loanId, originatorId); + originatorHelper.deleteOriginator(originatorId); + } + + @Test + public void testRetrieveLoanWithAllAssociationsIncludesOriginators() { + final String originatorExternalId = FeignLoanOriginatorHelper.generateUniqueExternalId(); + final Long originatorId = originatorHelper.createOriginator(originatorExternalId); + + final Long clientId = clientHelper.createClient("Test", "Client"); + final Long loanId = loanHelper.createSubmittedLoan(clientId); + + originatorHelper.attachOriginatorToLoan(loanId, originatorId); + + final var loanDetails = loanHelper.getLoanDetailsWithAssociationsAndExclude(loanId, "all", "guarantors,futureSchedule"); + + assertThat(loanDetails.getOriginators()).isNotNull(); + assertThat(loanDetails.getOriginators()).isNotEmpty(); + assertThat(loanDetails.getOriginators().get(0).getId()).isEqualTo(originatorId); + + originatorHelper.detachOriginatorFromLoan(loanId, originatorId); + originatorHelper.deleteOriginator(originatorId); + } + + @Test + public void testRetrieveLoanWithNoOriginatorsReturnsEmptyList() { + final Long clientId = clientHelper.createClient("Test", "Client"); + final Long loanId = loanHelper.createSubmittedLoan(clientId); + + final var loanDetails = loanHelper.getLoanDetailsWithAssociations(loanId, "originators"); + + assertThat(loanDetails.getOriginators()).isNotNull(); + assertThat(loanDetails.getOriginators()).isEmpty(); + } + + @Test + public void testRetrieveLoanExcludeOriginatorsFromAll() { + final String originatorExternalId = FeignLoanOriginatorHelper.generateUniqueExternalId(); + final Long originatorId = originatorHelper.createOriginator(originatorExternalId); + + final Long clientId = clientHelper.createClient("Test", "Client"); + final Long loanId = loanHelper.createSubmittedLoan(clientId); + + originatorHelper.attachOriginatorToLoan(loanId, originatorId); + + final var loanDetails = loanHelper.getLoanDetailsWithAssociationsAndExclude(loanId, "all", "originators,guarantors,futureSchedule"); + + assertThat(loanDetails.getOriginators()).isNull(); + + originatorHelper.detachOriginatorFromLoan(loanId, originatorId); + originatorHelper.deleteOriginator(originatorId); + } }