FINERACT-1981: Fix reschedule installment for progressive loans
This commit is contained in:
committed by
Adam Saghy
parent
9e32b3718d
commit
c50ed19959
+87
-5
@@ -55,6 +55,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
@@ -225,8 +226,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
final Integer installmentAmountInMultiplesOf = loan.getLoanProductRelatedDetail().getInstallmentAmountInMultiplesOf();
|
||||
ProgressiveLoanInterestScheduleModel scheduleModel = emiCalculator.generateInstallmentInterestScheduleModel(installments,
|
||||
LoanConfigurationDetailsMapper.map(loan), loanTermVariations, installmentAmountInMultiplesOf,
|
||||
overpaymentHolder.getMoneyObject().getMc());
|
||||
LoanConfigurationDetailsMapper.map(loan), installmentAmountInMultiplesOf, overpaymentHolder.getMoneyObject().getMc());
|
||||
ProgressiveTransactionCtx ctx = new ProgressiveTransactionCtx(currency, installments, charges, overpaymentHolder,
|
||||
changedTransactionDetail, scheduleModel);
|
||||
|
||||
@@ -236,8 +236,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
List<LoanTransaction> overpaidTransactions = new ArrayList<>();
|
||||
for (final ChangeOperation changeOperation : changeOperations) {
|
||||
if (changeOperation.isLoanTermVariationsData()) {
|
||||
final LoanTermVariationsData interestRateChange = changeOperation.getLoanTermVariationsData().get();
|
||||
processLoanTermVariation(installments, interestRateChange, scheduleModel);
|
||||
final LoanTermVariationsData termVariationsData = changeOperation.getLoanTermVariationsData().get();
|
||||
processLoanTermVariation(installments, termVariationsData, scheduleModel);
|
||||
} else if (changeOperation.isTransaction()) {
|
||||
LoanTransaction transaction = changeOperation.getLoanTransaction().get();
|
||||
if (loan.getStatus().isOverpaid() && transaction.isAccrualActivity()) {
|
||||
@@ -309,10 +309,92 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
case INTEREST_PAUSE -> handleInterestPause(installments, termVariationsData, scheduleModel);
|
||||
case INTEREST_RATE_FROM_INSTALLMENT -> handleChangeInterestRate(installments, termVariationsData, scheduleModel);
|
||||
case EXTEND_REPAYMENT_PERIOD -> handleExtraRepaymentPeriod(installments, termVariationsData, scheduleModel);
|
||||
case DUE_DATE -> handleDueDateChangeOnRepaymentPeriod(installments, termVariationsData, scheduleModel);
|
||||
default -> throw new IllegalStateException("Unhandled LoanTermVariationType.");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDueDateChangeOnRepaymentPeriod(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData termVariationsData, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final LocalDate targetRepaymentPeriodDueDate = termVariationsData.getTermVariationApplicableFrom();
|
||||
final LocalDate newDueDate = termVariationsData.getDateValue();
|
||||
final Loan loan = installments.getFirst().getLoan();
|
||||
final LoanApplicationTerms loanApplicationTerms = new LoanApplicationTerms.Builder() //
|
||||
.currency(loan.getCurrency().toData()) //
|
||||
.repaymentEvery(loan.getLoanProductRelatedDetail().getRepayEvery()) //
|
||||
.repaymentPeriodFrequencyType(loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType()) //
|
||||
.fixedLength(loan.getLoanProductRelatedDetail().getFixedLength()) //
|
||||
.seedDate(newDueDate) //
|
||||
.build();
|
||||
emiCalculator.changeDueDate(scheduleModel, loanApplicationTerms, targetRepaymentPeriodDueDate, newDueDate);
|
||||
|
||||
IntStream.range(0, installments.size()).filter(i -> installments.get(i).getDueDate().equals(targetRepaymentPeriodDueDate))
|
||||
.findFirst().ifPresent(targetInstallmentIndex -> {
|
||||
long scheduleModelStartIndex = installments.subList(0, targetInstallmentIndex).stream()
|
||||
.filter(inst -> !inst.isDownPayment() && !inst.isAdditional()).count();
|
||||
|
||||
for (int i = targetInstallmentIndex; i < installments.size(); i++) {
|
||||
final LoanRepaymentScheduleInstallment installment = installments.get(i);
|
||||
if (installment.isDownPayment() || installment.isAdditional()) {
|
||||
continue;
|
||||
}
|
||||
if (scheduleModelStartIndex >= scheduleModel.repaymentPeriods().size()) {
|
||||
break;
|
||||
}
|
||||
|
||||
final RepaymentPeriod repaymentPeriod = scheduleModel.repaymentPeriods().get((int) scheduleModelStartIndex);
|
||||
|
||||
if (isNotObligationsMet(installment)) {
|
||||
installment.updateFromDate(repaymentPeriod.getFromDate());
|
||||
installment.updateDueDate(repaymentPeriod.getDueDate());
|
||||
installment.updatePrincipal(repaymentPeriod.getDuePrincipal().getAmount());
|
||||
installment.updateInterestCharged(repaymentPeriod.getDueInterest().getAmount());
|
||||
}
|
||||
|
||||
scheduleModelStartIndex++;
|
||||
}
|
||||
});
|
||||
|
||||
mergeAdditionalInstallmentsBeforeMaturityDate(installments, scheduleModel, loan);
|
||||
|
||||
installments.sort(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate));
|
||||
int installmentNumber = 1;
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
installment.updateInstallmentNumber(installmentNumber++);
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeAdditionalInstallmentsBeforeMaturityDate(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final ProgressiveLoanInterestScheduleModel scheduleModel, final Loan loan) {
|
||||
final LocalDate newMaturityDate = scheduleModel.repaymentPeriods().getLast().getDueDate();
|
||||
|
||||
final Optional<LoanRepaymentScheduleInstallment> lastRegularInstallmentOptional = installments.stream() //
|
||||
.filter(i -> !i.isDownPayment() && !i.isAdditional() && !i.isReAged()) //
|
||||
.reduce((first, second) -> second);
|
||||
|
||||
lastRegularInstallmentOptional.ifPresent(lastRegularInstallment -> {
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
installments.stream() //
|
||||
.filter(i -> i.isAdditional() && i.getDueDate() != null && i.getDueDate().isBefore(newMaturityDate))
|
||||
.forEach(additionalInstallment -> {
|
||||
final Money mergedFees = lastRegularInstallment.getFeeChargesCharged(currency)
|
||||
.plus(additionalInstallment.getFeeChargesCharged(currency));
|
||||
lastRegularInstallment.setFeeChargesCharged(mergedFees.getAmount());
|
||||
|
||||
final Money mergedPenalties = lastRegularInstallment.getPenaltyChargesCharged(currency)
|
||||
.plus(additionalInstallment.getPenaltyChargesCharged(currency));
|
||||
lastRegularInstallment.setPenaltyCharges(mergedPenalties.getAmount());
|
||||
|
||||
additionalInstallment.getInstallmentCharges().forEach(charge -> {
|
||||
lastRegularInstallment.getInstallmentCharges().add(charge);
|
||||
charge.setInstallment(lastRegularInstallment);
|
||||
});
|
||||
});
|
||||
|
||||
installments.removeIf(i -> i.isAdditional() && i.getDueDate() != null && i.getDueDate().isBefore(newMaturityDate));
|
||||
});
|
||||
}
|
||||
|
||||
private void handleExtraRepaymentPeriod(final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final LoanTermVariationsData termVariationsData, final ProgressiveLoanInterestScheduleModel scheduleModel) {
|
||||
final LocalDate interestRateChangeSubmittedOnDate = termVariationsData.getTermVariationApplicableFrom();
|
||||
@@ -1266,7 +1348,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.collect(Collectors.groupingBy(ltvd -> LoanTermVariationType.fromInt(ltvd.getTermType().getId().intValue())));
|
||||
|
||||
Stream.of(LoanTermVariationType.INTEREST_RATE_FROM_INSTALLMENT, LoanTermVariationType.INTEREST_PAUSE,
|
||||
LoanTermVariationType.EXTEND_REPAYMENT_PERIOD).forEach(key -> {
|
||||
LoanTermVariationType.EXTEND_REPAYMENT_PERIOD, LoanTermVariationType.DUE_DATE).forEach(key -> {
|
||||
if (loanTermVariationsMap.get(key) != null) {
|
||||
changeOperations.addAll(loanTermVariationsMap.get(key).stream().map(ChangeOperation::new).toList());
|
||||
}
|
||||
|
||||
+1
-5
@@ -37,7 +37,6 @@ import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.OutstandingAmountsDTO;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
@@ -106,11 +105,8 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
// generate list of proposed schedule due dates
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc,
|
||||
periodStartDate, loanApplicationTerms, holidayDetailDTO);
|
||||
List<LoanTermVariationsData> loanTermVariations = loanApplicationTerms.getLoanTermVariations() != null
|
||||
? loanApplicationTerms.getLoanTermVariations().getExceptionData()
|
||||
: null;
|
||||
final ProgressiveLoanInterestScheduleModel interestScheduleModel = emiCalculator.generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(), loanTermVariations,
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(),
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
final List<LoanScheduleModelPeriod> periods = new ArrayList<>(expectedRepaymentPeriods.size());
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class InterestScheduleModelServiceGsonContext {
|
||||
public ProgressiveLoanInterestScheduleModel createProgressiveLoanInterestScheduleModelInstance(Type type) {
|
||||
if (type == ProgressiveLoanInterestScheduleModel.class) {
|
||||
setPrev(null);
|
||||
return new ProgressiveLoanInterestScheduleModel(new ArrayList<>(), getLoanProductRelatedDetail(), new ArrayList<>(),
|
||||
return new ProgressiveLoanInterestScheduleModel(new ArrayList<>(), getLoanProductRelatedDetail(),
|
||||
installmentAmountInMultipliesOf, getMc());
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported ProgressiveLoanInterestScheduleModel type: " + type);
|
||||
|
||||
+5
-4
@@ -26,7 +26,6 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
@@ -46,8 +45,7 @@ public interface EMICalculator {
|
||||
*/
|
||||
@NotNull
|
||||
ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List<LoanScheduleModelRepaymentPeriod> periods,
|
||||
@NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations,
|
||||
Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
@NotNull ILoanConfigurationDetails loanProductRelatedDetail, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
|
||||
/**
|
||||
* This method creates an Interest model with repayment periods from the installments which retrieved from the
|
||||
@@ -56,7 +54,7 @@ public interface EMICalculator {
|
||||
@NotNull
|
||||
ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel(
|
||||
@NotNull List<LoanRepaymentScheduleInstallment> installments, @NotNull ILoanConfigurationDetails loanProductRelatedDetail,
|
||||
List<LoanTermVariationsData> loanTermVariations, Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
Integer installmentAmountInMultiplesOf, MathContext mc);
|
||||
|
||||
/**
|
||||
* Find repayment period based on Due Date.
|
||||
@@ -169,4 +167,7 @@ public interface EMICalculator {
|
||||
EqualAmortizationValues calculateAdjustedEqualAmortizationValues(Money outstanding, Money total,
|
||||
Money sumOfOtherEqualAmortizationValues, Integer numberOfInstallments, Integer installmentAmountInMultiplesOf,
|
||||
MonetaryCurrency currency);
|
||||
|
||||
void changeDueDate(ProgressiveLoanInterestScheduleModel scheduleModel, LoanApplicationTerms loanApplicationTerms,
|
||||
LocalDate targetRepaymentPeriodDueDate, LocalDate newDueDate);
|
||||
}
|
||||
|
||||
+65
-15
@@ -45,9 +45,7 @@ import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
|
||||
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
|
||||
@@ -77,28 +75,26 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
@Override
|
||||
@NotNull
|
||||
public ProgressiveLoanInterestScheduleModel generatePeriodInterestScheduleModel(@NotNull List<LoanScheduleModelRepaymentPeriod> periods,
|
||||
@NotNull ILoanConfigurationDetails loanProductRelatedDetail, List<LoanTermVariationsData> loanTermVariations,
|
||||
final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
@NotNull ILoanConfigurationDetails loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf,
|
||||
final MathContext mc) {
|
||||
return generateInterestScheduleModel(periods, LoanScheduleModelRepaymentPeriod::periodFromDate,
|
||||
LoanScheduleModelRepaymentPeriod::periodDueDate, loanProductRelatedDetail, loanTermVariations,
|
||||
installmentAmountInMultiplesOf, mc);
|
||||
LoanScheduleModelRepaymentPeriod::periodDueDate, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public ProgressiveLoanInterestScheduleModel generateInstallmentInterestScheduleModel(
|
||||
@NotNull List<LoanRepaymentScheduleInstallment> installments, @NotNull ILoanConfigurationDetails loanProductRelatedDetail,
|
||||
List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
installments = installments.stream().filter(installment -> !installment.isDownPayment() && !installment.isAdditional()).toList();
|
||||
return generateInterestScheduleModel(installments, LoanRepaymentScheduleInstallment::getFromDate,
|
||||
LoanRepaymentScheduleInstallment::getDueDate, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf,
|
||||
mc);
|
||||
LoanRepaymentScheduleInstallment::getDueDate, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private <T> ProgressiveLoanInterestScheduleModel generateInterestScheduleModel(@NotNull List<T> periods, Function<T, LocalDate> from,
|
||||
Function<T, LocalDate> to, @NotNull ILoanConfigurationDetails loanProductRelatedDetail,
|
||||
List<LoanTermVariationsData> loanTermVariations, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final Money zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
final AtomicReference<RepaymentPeriod> prev = new AtomicReference<>();
|
||||
List<RepaymentPeriod> repaymentPeriods = periods.stream().map(e -> {
|
||||
@@ -106,8 +102,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
prev.set(rp);
|
||||
return rp;
|
||||
}).toList();
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, loanProductRelatedDetail, loanTermVariations,
|
||||
installmentAmountInMultiplesOf, mc);
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -566,8 +561,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, calculateFromRepaymentPeriodDueDate);
|
||||
if (onlyOnActualModelShouldApply && (scheduleModel.loanTermVariations() == null
|
||||
|| scheduleModel.loanTermVariations().get(LoanTermVariationType.DUE_DATE) == null)) {
|
||||
if (onlyOnActualModelShouldApply) {
|
||||
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods);
|
||||
}
|
||||
}
|
||||
@@ -638,6 +632,62 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
.reduce(scheduleModel.zero(), Money::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeDueDate(final ProgressiveLoanInterestScheduleModel scheduleModel, final LoanApplicationTerms loanApplicationTerms,
|
||||
final LocalDate targetRepaymentPeriodDueDate, LocalDate newDueDate) {
|
||||
final RepaymentPeriod targetRepaymentPeriod = scheduleModel.findRepaymentPeriod(targetRepaymentPeriodDueDate).orElseThrow();
|
||||
targetRepaymentPeriod.setDueDate(newDueDate);
|
||||
|
||||
if (!targetRepaymentPeriod.getInterestPeriods().isEmpty()) {
|
||||
final InterestPeriod lastInterestPeriod = targetRepaymentPeriod.getInterestPeriods().getLast();
|
||||
lastInterestPeriod.setDueDate(newDueDate);
|
||||
}
|
||||
|
||||
final int targetRepaymentPeriodIndex = scheduleModel.repaymentPeriods().indexOf(targetRepaymentPeriod);
|
||||
final int lastRepaymentPeriodIndex = scheduleModel.repaymentPeriods().size() - 1;
|
||||
final LocalDate originalNewDueDate = newDueDate;
|
||||
for (int i = targetRepaymentPeriodIndex + 1; i < scheduleModel.repaymentPeriods().size(); ++i) {
|
||||
final RepaymentPeriod nextRepaymentPeriod = scheduleModel.repaymentPeriods().get(i);
|
||||
nextRepaymentPeriod.setFromDate(newDueDate);
|
||||
|
||||
LocalDate nextRepaymentDate = scheduledDateGenerator.generateNextRepaymentDate(newDueDate, loanApplicationTerms, false);
|
||||
|
||||
if (i == lastRepaymentPeriodIndex && loanApplicationTerms.getFixedLength() != null) {
|
||||
nextRepaymentDate = calculateDateForFixedLength(scheduleModel, loanApplicationTerms, targetRepaymentPeriodDueDate,
|
||||
originalNewDueDate);
|
||||
}
|
||||
nextRepaymentPeriod.setDueDate(nextRepaymentDate);
|
||||
|
||||
if (!nextRepaymentPeriod.getInterestPeriods().isEmpty()) {
|
||||
nextRepaymentPeriod.getInterestPeriods().getFirst().setFromDate(newDueDate);
|
||||
final InterestPeriod lastInterestPeriod = nextRepaymentPeriod.getInterestPeriods().getLast();
|
||||
lastInterestPeriod.setDueDate(nextRepaymentDate);
|
||||
}
|
||||
|
||||
newDueDate = nextRepaymentDate;
|
||||
}
|
||||
|
||||
final List<RepaymentPeriod> relatedRepaymentPeriods = scheduleModel.getRelatedRepaymentPeriods(targetRepaymentPeriodDueDate);
|
||||
calculateRateFactorForPeriods(relatedRepaymentPeriods, scheduleModel);
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, targetRepaymentPeriodDueDate);
|
||||
}
|
||||
|
||||
private LocalDate calculateDateForFixedLength(final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
final LoanApplicationTerms loanApplicationTerms, final LocalDate targetRepaymentPeriodDueDate, final LocalDate newDueDate) {
|
||||
final long variationDays = DateUtils.getDifferenceInDays(targetRepaymentPeriodDueDate, newDueDate);
|
||||
final LocalDate repaymentStartDate = scheduleModel.repaymentPeriods().getFirst().getFromDate();
|
||||
|
||||
return switch (loanApplicationTerms.getRepaymentPeriodFrequencyType()) {
|
||||
case DAYS -> repaymentStartDate.plusDays(loanApplicationTerms.getFixedLength() + variationDays);
|
||||
case WEEKS -> repaymentStartDate.plusWeeks(loanApplicationTerms.getFixedLength() + variationDays);
|
||||
case MONTHS -> repaymentStartDate.plusMonths(loanApplicationTerms.getFixedLength() + variationDays);
|
||||
case YEARS -> repaymentStartDate.plusYears(loanApplicationTerms.getFixedLength() + variationDays);
|
||||
case INVALID, WHOLE_TERM -> throw new IllegalArgumentException(
|
||||
"Unsupported period frequency type for fixed length: " + loanApplicationTerms.getRepaymentPeriodFrequencyType());
|
||||
};
|
||||
}
|
||||
|
||||
private List<RepaymentPeriod> findPossiblyOverdueRepaymentPeriods(final LocalDate targetDate,
|
||||
final ProgressiveLoanInterestScheduleModel model) {
|
||||
return model.repaymentPeriods().stream() //
|
||||
@@ -767,7 +817,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = scheduledDateGenerator.generateRepaymentPeriods(mc,
|
||||
periodStartDate, loanApplicationTerms, null);
|
||||
final ProgressiveLoanInterestScheduleModel temporaryReAgedScheduleModel = generatePeriodInterestScheduleModel(
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(), null,
|
||||
expectedRepaymentPeriods, loanApplicationTerms.toLoanConfigurationDetails(),
|
||||
loanApplicationTerms.getInstallmentAmountInMultiplesOf(), mc);
|
||||
|
||||
addDisbursement(temporaryReAgedScheduleModel, EmiChangeOperation.disburse(periodStartDate, loanApplicationTerms.getPrincipal()));
|
||||
|
||||
+5
-22
@@ -40,7 +40,6 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Setter;
|
||||
@@ -49,8 +48,6 @@ import org.apache.fineract.infrastructure.core.serialization.gson.JsonExclude;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetails;
|
||||
|
||||
@Data
|
||||
@@ -62,7 +59,6 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
private final TreeSet<InterestRate> interestRates;
|
||||
@JsonExclude
|
||||
private final ILoanConfigurationDetails loanProductRelatedDetail;
|
||||
private final Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariations;
|
||||
private final Integer installmentAmountInMultiplesOf;
|
||||
@JsonExclude
|
||||
private final MathContext mc;
|
||||
@@ -74,12 +70,10 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
private LocalDate lastOverdueBalanceChange;
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repaymentPeriods,
|
||||
final ILoanConfigurationDetails loanProductRelatedDetail, final List<LoanTermVariationsData> loanTermVariations,
|
||||
final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
final ILoanConfigurationDetails loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc) {
|
||||
this.repaymentPeriods = new ArrayList<>(repaymentPeriods);
|
||||
this.interestRates = new TreeSet<>(Collections.reverseOrder());
|
||||
this.loanProductRelatedDetail = loanProductRelatedDetail;
|
||||
this.loanTermVariations = buildLoanTermVariationMap(loanTermVariations);
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.mc = mc;
|
||||
this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
@@ -88,15 +82,13 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
}
|
||||
|
||||
private ProgressiveLoanInterestScheduleModel(final List<RepaymentPeriod> repaymentPeriods, final TreeSet<InterestRate> interestRates,
|
||||
final ILoanConfigurationDetails loanProductRelatedDetail,
|
||||
final Map<LoanTermVariationType, List<LoanTermVariationsData>> loanTermVariations, final Integer installmentAmountInMultiplesOf,
|
||||
final MathContext mc, final boolean isCopiedForCalculation) {
|
||||
final ILoanConfigurationDetails loanProductRelatedDetail, final Integer installmentAmountInMultiplesOf, final MathContext mc,
|
||||
final boolean isCopiedForCalculation) {
|
||||
this.mc = mc;
|
||||
this.repaymentPeriods = copyRepaymentPeriods(repaymentPeriods,
|
||||
(previousPeriod, repaymentPeriod) -> RepaymentPeriod.copy(previousPeriod, repaymentPeriod, mc));
|
||||
this.interestRates = new TreeSet<>(interestRates);
|
||||
this.loanProductRelatedDetail = loanProductRelatedDetail;
|
||||
this.loanTermVariations = loanTermVariations;
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.zero = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
modifiers = new HashMap<>(Map.of(EMI_RECALCULATION, true, COPY, isCopiedForCalculation, INTEREST_RECALCULATION_ENABLED,
|
||||
@@ -104,14 +96,14 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
}
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel deepCopy(MathContext mc) {
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, interestRates, loanProductRelatedDetail, loanTermVariations,
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriods, interestRates, loanProductRelatedDetail,
|
||||
installmentAmountInMultiplesOf, mc, false);
|
||||
}
|
||||
|
||||
public ProgressiveLoanInterestScheduleModel copyWithoutPaidAmounts() {
|
||||
final List<RepaymentPeriod> repaymentPeriodCopies = copyRepaymentPeriods(repaymentPeriods,
|
||||
(previousPeriod, repaymentPeriod) -> RepaymentPeriod.copyWithoutPaidAmounts(previousPeriod, repaymentPeriod, mc));
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriodCopies, interestRates, loanProductRelatedDetail, loanTermVariations,
|
||||
return new ProgressiveLoanInterestScheduleModel(repaymentPeriodCopies, interestRates, loanProductRelatedDetail,
|
||||
installmentAmountInMultiplesOf, mc, true);
|
||||
}
|
||||
|
||||
@@ -416,15 +408,6 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
: date.isAfter(previousInterestPeriod.getDueDate()) ? previousInterestPeriod.getDueDate() : date;
|
||||
}
|
||||
|
||||
private Map<LoanTermVariationType, List<LoanTermVariationsData>> buildLoanTermVariationMap(
|
||||
final List<LoanTermVariationsData> loanTermVariationsData) {
|
||||
if (loanTermVariationsData == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return loanTermVariationsData.stream()
|
||||
.collect(Collectors.groupingBy(ltvd -> LoanTermVariationType.fromInt(ltvd.getTermType().getId().intValue())));
|
||||
}
|
||||
|
||||
public void disableEMIRecalculation() {
|
||||
this.modifiers.put(EMI_RECALCULATION, false);
|
||||
}
|
||||
|
||||
+4
-3
@@ -44,8 +44,9 @@ public class RepaymentPeriod {
|
||||
|
||||
@JsonExclude
|
||||
private final RepaymentPeriod previous;
|
||||
@Setter
|
||||
@Getter
|
||||
private final LocalDate fromDate;
|
||||
private LocalDate fromDate;
|
||||
@Setter
|
||||
@Getter
|
||||
private LocalDate dueDate;
|
||||
@@ -97,10 +98,10 @@ public class RepaymentPeriod {
|
||||
private boolean noUnrecognisedInterest;
|
||||
@Setter
|
||||
@Getter
|
||||
private boolean reAged = false;
|
||||
private boolean reAged;
|
||||
@Setter
|
||||
@Getter
|
||||
private boolean reAgedEarlyRepaymentHolder = false;
|
||||
private boolean reAgedEarlyRepaymentHolder;
|
||||
@Getter
|
||||
@Setter
|
||||
private Money reAgedInterest;
|
||||
|
||||
Reference in New Issue
Block a user