Skip to content

Commit

Permalink
FINERACT-2081: Add all loan term validations to loan details
Browse files Browse the repository at this point in the history
  • Loading branch information
oleksii-novikov-onix committed Oct 3, 2024
1 parent 33f666c commit 8b38def
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 86 deletions.
11 changes: 11 additions & 0 deletions fineract-avro-schemas/src/main/avro/loan/v1/LoanAccountDataV1.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,17 @@
}
]
},
{
"default": null,
"name": "loanTermVariations",
"type": [
"null",
{
"type": "array",
"items": "org.apache.fineract.avro.loan.v1.LoanTermVariationsDataV1"
}
]
},
{
"default": null,
"name": "clientActiveLoanOptions",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ private DataTableApiConstant() {
public static final String linkedAccountAssociateParamName = "linkedAccount";
public static final String multiDisburseDetailsAssociateParamName = "multiDisburseDetails";
public static final String futureScheduleAssociateParamName = "futureSchedule";
public static final String loanTermVariationsAssociateParamName = "loanTermVariations";
public static final String meetingAssociateParamName = "meeting";
public static final String emiAmountVariationsAssociateParamName = "emiAmountVariations";
public static final String collectionAssociateParamName = "collection";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;

@Getter
public class LoanTermVariationsData implements Comparable<LoanTermVariationsData> {
Expand All @@ -46,6 +47,16 @@ public LoanTermVariationsData(final Long id, final EnumOptionData termType, fina
this.isSpecificToInstallment = isSpecificToInstallment;
}

public LoanTermVariationsData(final Long id, final Integer termType, final LocalDate termVariationApplicableFrom,
final BigDecimal decimalValue, final LocalDate dateValue, final boolean isSpecificToInstallment) {
this.id = id;
this.termType = LoanEnumerations.loanVariationType(LoanTermVariationType.fromInt(termType));
this.termVariationApplicableFrom = termVariationApplicableFrom;
this.decimalValue = decimalValue;
this.dateValue = dateValue;
this.isSpecificToInstallment = isSpecificToInstallment;
}

public LoanTermVariationsData(final EnumOptionData termType, final LocalDate termVariationApplicableFrom, final BigDecimal decimalValue,
LocalDate dateValue, final boolean isSpecificToInstallment) {
this.id = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5372,16 +5372,12 @@ public boolean isForeclosure() {
return isForeClosure;
}

public Set<LoanTermVariations> getActiveLoanTermVariations() {
Set<LoanTermVariations> retData = new HashSet<>();
if (this.loanTermVariations != null && !this.loanTermVariations.isEmpty()) {
for (LoanTermVariations loanTermVariations : this.loanTermVariations) {
if (loanTermVariations.isActive()) {
retData.add(loanTermVariations);
}
}
public List<LoanTermVariations> getActiveLoanTermVariations() {
if (this.loanTermVariations == null) {
return new ArrayList<>();
}
return !retData.isEmpty() ? retData : null;

return this.loanTermVariations.stream().filter(LoanTermVariations::isActive).collect(Collectors.toList());
}

public boolean isTopup() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain;

import java.util.List;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariations;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
Expand All @@ -28,7 +29,24 @@
public interface LoanTermVariationsRepository
extends JpaRepository<LoanTermVariations, Long>, JpaSpecificationExecutor<LoanTermVariations> {

@Query("select lrr from LoanTermVariations lrr where lrr.loan.id = :loanId")
List<LoanTermVariations> findAllLoanTermVariationsByLoanId(@Param("loanId") Long loanId);
@Query("""
select new org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData(
ltv.id, ltv.termType, ltv.termApplicableFrom, ltv.decimalValue, ltv.dateValue, ltv.isSpecificToInstallment
)
from LoanTermVariations ltv
where ltv.loan.id = :loanId
order by ltv.termApplicableFrom
""")
List<LoanTermVariationsData> findLoanTermVariationsByLoanId(@Param("loanId") long loanId);

@Query("""
select new org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData(
ltv.id, ltv.termType, ltv.termApplicableFrom, ltv.decimalValue, ltv.dateValue, ltv.isSpecificToInstallment
)
from LoanTermVariations ltv
where ltv.loan.id = :loanId and ltv.termType = :termType
order by ltv.termApplicableFrom
""")
List<LoanTermVariationsData> findLoanTermVariationsByLoanIdAndTermType(@Param("loanId") long loanId, @Param("termType") int termType);

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.util.Collection;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.avro.generic.GenericContainer;
import org.apache.commons.collections4.CollectionUtils;
Expand Down Expand Up @@ -86,9 +85,10 @@ public <T> ByteBufferSerializable toAvroDTO(BusinessEvent<T> rawEvent) {
List<LoanInstallmentDelinquencyBucketDataV1> installmentsDelinquencyData = installmentLevelDelinquencyEventProducer
.calculateInstallmentLevelDelinquencyData(event.get(), data.getCurrency());

Set<LoanTermVariations> activeLoanTermVariations = event.get().getActiveLoanTermVariations();
if (activeLoanTermVariations != null) {
data.setEmiAmountVariations(activeLoanTermVariations.stream().map(LoanTermVariations::toData).toList());
List<LoanTermVariations> activeLoanTermVariations = event.get().getActiveLoanTermVariations();

if (!activeLoanTermVariations.isEmpty()) {
data.setLoanTermVariations(activeLoanTermVariations.stream().map(LoanTermVariations::toData).toList());
}

LoanAccountDataV1 result = mapper.map(data);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService;
import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
Expand Down Expand Up @@ -287,6 +288,7 @@ public class LoansApiResource {
private final SqlValidator sqlValidator;
private final LoanSummaryBalancesRepository loanSummaryBalancesRepository;
private final ClientReadPlatformService clientReadPlatformService;
private final LoanTermVariationsRepository loanTermVariationsRepository;

/*
* This template API is used for loan approval, ideally this should be invoked on loan that are pending for
Expand Down Expand Up @@ -897,7 +899,8 @@ private String retrieveLoan(final Long loanId, final String loanExternalIdStr, b
Collection<NoteData> notes = null;
PortfolioAccountData linkedAccount = null;
Collection<DisbursementData> disbursementData = null;
Collection<LoanTermVariationsData> emiAmountVariations = null;
List<LoanTermVariationsData> emiAmountVariations = null;
List<LoanTermVariationsData> loanTermVariations = null;
Collection<LoanCollateralResponseData> loanCollateralManagements;
Collection<LoanCollateralManagementData> loanCollateralManagementData = new ArrayList<>();
CollectionData collectionData = this.delinquencyReadPlatformService.calculateLoanCollectionData(resolvedLoanId);
Expand All @@ -911,7 +914,8 @@ private String retrieveLoan(final Long loanId, final String loanExternalIdStr, b
DataTableApiConstant.transactionsAssociateParamName, DataTableApiConstant.chargesAssociateParamName,
DataTableApiConstant.guarantorsAssociateParamName, DataTableApiConstant.collateralAssociateParamName,
DataTableApiConstant.notesAssociateParamName, DataTableApiConstant.linkedAccountAssociateParamName,
DataTableApiConstant.multiDisburseDetailsAssociateParamName, DataTableApiConstant.collectionAssociateParamName));
DataTableApiConstant.multiDisburseDetailsAssociateParamName, DataTableApiConstant.collectionAssociateParamName,
DataTableApiConstant.loanTermVariationsAssociateParamName));
}

ApiParameterHelper.excludeAssociationsForResponseIfProvided(exclude, associationParameters);
Expand All @@ -938,10 +942,15 @@ private String retrieveLoan(final Long loanId, final String loanExternalIdStr, b
if (associationParameters.contains(DataTableApiConstant.emiAmountVariationsAssociateParamName)
|| associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.emiAmountVariationsAssociateParamName);
emiAmountVariations = this.loanReadPlatformService.retrieveLoanTermVariations(resolvedLoanId,
emiAmountVariations = this.loanTermVariationsRepository.findLoanTermVariationsByLoanIdAndTermType(resolvedLoanId,
LoanTermVariationType.EMI_AMOUNT.getValue());
}

if (associationParameters.contains(DataTableApiConstant.loanTermVariationsAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.loanTermVariationsAssociateParamName);
loanTermVariations = this.loanTermVariationsRepository.findLoanTermVariationsByLoanId(resolvedLoanId);
}

if (associationParameters.contains(DataTableApiConstant.repaymentScheduleAssociateParamName)) {
mandatoryResponseParameters.add(DataTableApiConstant.repaymentScheduleAssociateParamName);
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = new RepaymentScheduleRelatedLoanData(
Expand Down Expand Up @@ -1118,7 +1127,8 @@ private String retrieveLoan(final Long loanId, final String loanExternalIdStr, b
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods, clientActiveLoanOptions, rates, isRatesEnabled, collectionData,
LoanScheduleType.getValuesAsEnumOptionDataList(), LoanScheduleProcessingType.getValuesAsEnumOptionDataList());
LoanScheduleType.getValuesAsEnumOptionDataList(), LoanScheduleProcessingType.getValuesAsEnumOptionDataList(),
loanTermVariations);

final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
mandatoryResponseParameters);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ private GetLoansLoanIdFeeFrequency() {}
public Double maxOutstandingLoanBalance;
@Schema(example = "false")
public Boolean canDisburse;
public Set<GetLoansLoanIdEmiVariations> emiAmountVariations;
public List<GetLoansLoanIdEmiVariations> emiAmountVariations;
public List<GetLoansLoanIdEmiVariations> loanTermVariations;
@Schema(example = "true")
public Boolean inArrears;
@Schema(example = "false")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ public class LoanAccountData {
private Boolean canDisburse;

private Collection<LoanTermVariationsData> emiAmountVariations;
private Collection<LoanTermVariationsData> loanTermVariations;
private Collection<LoanAccountSummaryData> clientActiveLoanOptions;
private Boolean canUseForTopup;
// TODO: avoid prefix "is"
Expand Down Expand Up @@ -521,7 +522,7 @@ public LoanAccountData associationsAndTemplate(final LoanScheduleData repaymentS
final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods,
final Collection<LoanAccountSummaryData> clientActiveLoanOptions, final List<RateData> rates, final Boolean isRatesEnabled,
final CollectionData delinquent, final List<EnumOptionData> loanScheduleTypeOptions,
final List<EnumOptionData> loanScheduleProcessingTypeOptions) {
final List<EnumOptionData> loanScheduleProcessingTypeOptions, final List<LoanTermVariationsData> loanTermVariations) {

// TODO: why are these variables 'calendarData', 'chargeTemplate' never used (see original private constructor)

Expand All @@ -541,7 +542,7 @@ public LoanAccountData associationsAndTemplate(final LoanScheduleData repaymentS
.setOverdueCharges(overdueCharges).setPaidInAdvance(paidInAdvance).setInterestRatesPeriods(interestRatesPeriods)
.setClientActiveLoanOptions(clientActiveLoanOptions).setRates(rates).setIsRatesEnabled(isRatesEnabled)
.setDelinquent(delinquent).setLoanScheduleTypeOptions(loanScheduleTypeOptions)
.setLoanScheduleProcessingTypeOptions(loanScheduleProcessingTypeOptions);
.setLoanScheduleProcessingTypeOptions(loanScheduleProcessingTypeOptions).setLoanTermVariations(loanTermVariations);
}

public LoanAccountData associationsAndTemplate(final Collection<LoanProductData> productOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,9 @@ public CommandProcessingResult approve(JsonCommand jsonCommand) {
final LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(scheduleGeneratorDTO);

LocalDate rescheduleFromDate = null;
Set<LoanTermVariations> activeLoanTermVariations = loan.getActiveLoanTermVariations();
List<LoanTermVariations> activeLoanTermVariations = loan.getActiveLoanTermVariations();
LoanTermVariations dueDateVariationInCurrentRequest = loanRescheduleRequest.getDueDateTermVariationIfExists();
if (dueDateVariationInCurrentRequest != null && activeLoanTermVariations != null) {
if (dueDateVariationInCurrentRequest != null && !activeLoanTermVariations.isEmpty()) {
LocalDate fromScheduleDate = dueDateVariationInCurrentRequest.fetchTermApplicaDate();
LocalDate currentScheduleDate = fromScheduleDate;
LocalDate modifiedScheduleDate = dueDateVariationInCurrentRequest.fetchDateValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import org.apache.fineract.portfolio.loanaccount.data.LoanApprovalData;
import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
Expand Down Expand Up @@ -99,8 +98,6 @@ LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelated

DisbursementData retrieveLoanDisbursementDetail(Long loanId, Long disbursementId);

Collection<LoanTermVariationsData> retrieveLoanTermVariations(Long loanId, Integer termType);

Collection<LoanScheduleAccrualData> retriveScheduleAccrualData();

LoanTransactionData retrieveRecoveryPaymentTemplate(Long loanId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@
import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
import org.apache.fineract.portfolio.loanaccount.data.LoanStatusEnumData;
import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
Expand All @@ -114,7 +113,6 @@
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
Expand Down Expand Up @@ -1740,33 +1738,6 @@ public DisbursementData retrieveLoanDisbursementDetail(Long loanId, Long disburs
return this.jdbcTemplate.queryForObject(sql, rm, loanId, disbursementId); // NOSONAR
}

@Override
public Collection<LoanTermVariationsData> retrieveLoanTermVariations(Long loanId, Integer termType) {
final LoanTermVariationsMapper rm = new LoanTermVariationsMapper();
final String sql = "select " + rm.schema() + " where tv.loan_id=? and tv.term_type=?";
return this.jdbcTemplate.query(sql, rm, loanId, termType); // NOSONAR
}

private static final class LoanTermVariationsMapper implements RowMapper<LoanTermVariationsData> {

public String schema() {
return "tv.id as id,tv.applicable_date as variationApplicableFrom,tv.decimal_value as decimalValue, tv.date_value as dateValue, tv.is_specific_to_installment as isSpecificToInstallment "
+ "from m_loan_term_variations tv";
}

@Override
public LoanTermVariationsData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final LocalDate variationApplicableFrom = JdbcSupport.getLocalDate(rs, "variationApplicableFrom");
final BigDecimal decimalValue = rs.getBigDecimal("decimalValue");
final LocalDate dateValue = JdbcSupport.getLocalDate(rs, "dateValue");
final boolean isSpecificToInstallment = rs.getBoolean("isSpecificToInstallment");

return new LoanTermVariationsData(id, LoanEnumerations.loanVariationType(LoanTermVariationType.EMI_AMOUNT),
variationApplicableFrom, decimalValue, dateValue, isSpecificToInstallment);
}
}

@Override
public Collection<LoanScheduleAccrualData> retriveScheduleAccrualData() {
final String chargeAccrualDateCriteria = configurationDomainService.getAccrualDateConfigForCharge();
Expand Down
Loading

0 comments on commit 8b38def

Please sign in to comment.