FINERACT-2418: add originatior association

This commit is contained in:
Attila Budai
2026-02-03 14:58:28 +01:00
committed by Adam Saghy
parent 3b5ea96f5e
commit ef12056e5d
7 changed files with 99 additions and 4 deletions
@@ -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";
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
@@ -16,6 +17,7 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -50,6 +50,7 @@ import org.apache.fineract.portfolio.group.data.GroupGeneralData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.guarantor.data.IGuarantor;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.apache.fineract.portfolio.loanorigination.data.LoanOriginatorData;
import org.apache.fineract.portfolio.loanproduct.data.LoanProductBorrowerCycleVariationData;
import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
@@ -151,6 +152,7 @@ public class LoanAccountData {
private Collection<NoteData> notes;
private Collection<DisbursementData> disbursementDetails;
private LoanScheduleData originalSchedule;
private Collection<LoanOriginatorData> originators;
// template
private Collection<LoanProductData> productOptions;
private Collection<StaffData> loanOfficerOptions;
@@ -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> 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<LoanOriginatorData> originatorList = loanOriginatorReadPlatformService.get().retrieveByLoanId(resolvedLoanId);
loanBasicDetails.setOriginators(originatorList.isEmpty() ? Collections.emptyList() : originatorList);
}
}
}
Collection<LoanProductData> productOptions = null;
@@ -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")));
@@ -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);
}
}