FINERACT-1981: Progressive loan schedule handling
This commit is contained in:
committed by
Arnold Gálovics
parent
71d2e51051
commit
2ea23be9a4
+13
-15
@@ -1289,7 +1289,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
updateLoanScheduleDependentDerivedFields();
|
||||
updateLoanSummaryDerivedFields();
|
||||
applyAccurals();
|
||||
|
||||
}
|
||||
|
||||
public void updateLoanSchedule(final Collection<LoanRepaymentScheduleInstallment> installments) {
|
||||
@@ -2530,9 +2529,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
|
||||
if ((interestRecalculationEnabledParam && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || disbursementMissedParam))) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
} else if (downPaymentEnabled) {
|
||||
LoanScheduleDTO loanSchedule = getRecalculatedSchedule(scheduleGeneratorDTO);
|
||||
updateLoanSchedule(loanSchedule.getInstallments());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2827,11 +2823,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
final MathContext mc = new MathContext(8, roundingMode);
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
LoanScheduleGenerator loanScheduleGenerator = null;
|
||||
LoanScheduleGenerator loanScheduleGenerator;
|
||||
if (loanApplicationTerms.isEqualAmortization()) {
|
||||
if (loanApplicationTerms.getInterestMethod().isDecliningBalance()) {
|
||||
final LoanScheduleGenerator decliningLoanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory()
|
||||
.create(InterestMethod.DECLINING_BALANCE);
|
||||
.create(loanApplicationTerms.getLoanScheduleType(), InterestMethod.DECLINING_BALANCE);
|
||||
Set<LoanCharge> loanCharges = getActiveCharges();
|
||||
LoanScheduleModel loanSchedule = decliningLoanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges,
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
@@ -2840,9 +2836,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
.updateTotalInterestDue(Money.of(loanApplicationTerms.getCurrency(), loanSchedule.getTotalInterestCharged()));
|
||||
|
||||
}
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(InterestMethod.FLAT);
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getLoanScheduleType(),
|
||||
InterestMethod.FLAT);
|
||||
} else {
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getInterestMethod());
|
||||
loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(loanApplicationTerms.getLoanScheduleType(),
|
||||
loanApplicationTerms.getInterestMethod());
|
||||
}
|
||||
|
||||
return loanScheduleGenerator.generate(mc, loanApplicationTerms, getActiveCharges(), scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
@@ -3401,8 +3399,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
|
||||
List<LoanTransaction> trans = getLoanTransactions();
|
||||
for (final LoanTransaction transaction : trans) {
|
||||
if (transaction.isNotReversed()
|
||||
&& (transaction.isChargeOff() || !(transaction.isDisbursement() || transaction.isNonMonetaryTransaction()))) {
|
||||
if (transaction.isNotReversed() && (transaction.isChargeOff() || !transaction.isNonMonetaryTransaction())) {
|
||||
repaymentsOrWaivers.add(transaction);
|
||||
}
|
||||
}
|
||||
@@ -3413,9 +3410,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
public List<LoanTransaction> retrieveListOfTransactionsPostDisbursementExcludeAccruals() {
|
||||
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
|
||||
for (final LoanTransaction transaction : this.loanTransactions) {
|
||||
if (transaction.isNotReversed()
|
||||
&& !(transaction.isDisbursement() || transaction.isAccrual() || transaction.isRepaymentAtDisbursement()
|
||||
|| transaction.isNonMonetaryTransaction() || transaction.isIncomePosting())) {
|
||||
if (transaction.isNotReversed() && !(transaction.isAccrual() || transaction.isRepaymentAtDisbursement()
|
||||
|| transaction.isNonMonetaryTransaction() || transaction.isIncomePosting())) {
|
||||
repaymentsOrWaivers.add(transaction);
|
||||
}
|
||||
}
|
||||
@@ -5754,7 +5750,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return null;
|
||||
}
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory().create(interestMethod);
|
||||
final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory()
|
||||
.create(this.loanRepaymentScheduleDetail.getLoanScheduleType(), interestMethod);
|
||||
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mc = new MathContext(19, roundingMode);
|
||||
@@ -5778,7 +5775,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
|
||||
|
||||
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod);
|
||||
final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory()
|
||||
.create(loanApplicationTerms.getLoanScheduleType(), interestMethod);
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategyCode);
|
||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, this,
|
||||
|
||||
+11
-5
@@ -419,13 +419,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
this.penaltyAccrued = null;
|
||||
}
|
||||
|
||||
public void resetChargesFields() {
|
||||
public void resetChargesCharged() {
|
||||
this.feeChargesCharged = null;
|
||||
this.feeChargesWaived = null;
|
||||
this.feeChargesWrittenOff = null;
|
||||
this.penaltyCharges = null;
|
||||
this.penaltyChargesWaived = null;
|
||||
this.penaltyChargesWrittenOff = null;
|
||||
}
|
||||
|
||||
public Money payPenaltyChargesComponent(final LocalDate transactionDate, final Money transactionAmountRemaining) {
|
||||
@@ -656,6 +652,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
this.penaltyCharges = defaultToNullIfZero(penaltyChargesDue.plus(this.penaltyCharges).getAmount());
|
||||
this.penaltyChargesWaived = defaultToNullIfZero(penaltyChargesWaived.plus(this.penaltyChargesWaived).getAmount());
|
||||
this.penaltyChargesWrittenOff = defaultToNullIfZero(penaltyChargesWrittenOff.plus(this.penaltyChargesWrittenOff).getAmount());
|
||||
checkIfRepaymentPeriodObligationsAreMet(getObligationsMetOnDate(), feeChargesDue.getCurrency());
|
||||
}
|
||||
|
||||
public void updateAccrualPortion(final Money interest, final Money feeCharges, final Money penalityCharges) {
|
||||
@@ -961,4 +958,13 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
return isDownPayment;
|
||||
}
|
||||
|
||||
public void resetBalances() {
|
||||
resetDerivedComponents();
|
||||
resetPrincipalDue();
|
||||
resetChargesCharged();
|
||||
}
|
||||
|
||||
public void resetPrincipalDue() {
|
||||
this.principal = null;
|
||||
}
|
||||
}
|
||||
|
||||
+29
-17
@@ -218,6 +218,7 @@ public final class LoanApplicationTerms {
|
||||
private LocalDate submittedOnDate;
|
||||
private boolean isScheduleExtensionForDownPaymentDisabled;
|
||||
private Money disbursedPrincipal;
|
||||
private final LoanScheduleType loanScheduleType;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
@@ -244,7 +245,8 @@ public final class LoanApplicationTerms {
|
||||
final BigDecimal fixedPrincipalPercentagePerInstallment, final boolean isPrincipalCompoundingDisabledForOverdueLoans,
|
||||
final Boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||
final Boolean isAutoRepaymentForDownPaymentEnabled, final RepaymentStartDateType repaymentStartDateType,
|
||||
final LocalDate submittedOnDate, final Boolean isScheduleExtensionForDownPaymentDisabled) {
|
||||
final LocalDate submittedOnDate, final Boolean isScheduleExtensionForDownPaymentDisabled,
|
||||
final LoanScheduleType loanScheduleType) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
|
||||
@@ -262,7 +264,8 @@ public final class LoanApplicationTerms {
|
||||
isSkipRepaymentOnFirstDayOfMonth, holidayDetailDTO, allowCompoundingOnEod, isEqualAmortization, false,
|
||||
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
||||
isPrincipalCompoundingDisabledForOverdueLoans, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, isScheduleExtensionForDownPaymentDisabled);
|
||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, isScheduleExtensionForDownPaymentDisabled,
|
||||
loanScheduleType);
|
||||
|
||||
}
|
||||
|
||||
@@ -318,6 +321,7 @@ public final class LoanApplicationTerms {
|
||||
isAutoRepaymentForDownPaymentEnabled = loanProductRelatedDetail.isEnableAutoRepaymentForDownPayment();
|
||||
isScheduleExtensionForDownPaymentDisabled = loanProductRelatedDetail.isDisableScheduleExtensionForDownPayment();
|
||||
}
|
||||
LoanScheduleType loanScheduleType = loanProductRelatedDetail.getLoanScheduleType();
|
||||
return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||
repaymentEvery, repaymentPeriodFrequencyType, ((nthDay != null) ? nthDay.getValue() : null), dayOfWeek, amortizationMethod,
|
||||
interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate,
|
||||
@@ -333,7 +337,8 @@ public final class LoanApplicationTerms {
|
||||
allowCompoundingOnEod, isEqualAmortization, isFirstRepaymentDateAllowedOnHoliday,
|
||||
isInterestToBeRecoveredFirstWhenGreaterThanEMI, fixedPrincipalPercentagePerInstallment,
|
||||
isPrincipalCompoundingDisabledForOverdueLoans, isDownPaymentEnabled, disbursedAmountPercentageForDownPayment,
|
||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, isScheduleExtensionForDownPaymentDisabled);
|
||||
isAutoRepaymentForDownPaymentEnabled, repaymentStartDateType, submittedOnDate, isScheduleExtensionForDownPaymentDisabled,
|
||||
loanScheduleType);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -362,7 +367,7 @@ public final class LoanApplicationTerms {
|
||||
final boolean isPrincipalCompoundingDisabledForOverdueLoans, final boolean isDownPaymentEnabled,
|
||||
final BigDecimal disbursedAmountPercentageForDownPayment, final boolean isAutoRepaymentForDownPaymentEnabled,
|
||||
final RepaymentStartDateType repaymentStartDateType, final LocalDate submittedOnDate,
|
||||
final boolean isScheduleExtensionForDownPaymentDisabled) {
|
||||
final boolean isScheduleExtensionForDownPaymentDisabled, final LoanScheduleType loanScheduleType) {
|
||||
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
@@ -446,6 +451,7 @@ public final class LoanApplicationTerms {
|
||||
this.repaymentStartDateType = repaymentStartDateType;
|
||||
this.submittedOnDate = submittedOnDate;
|
||||
this.isScheduleExtensionForDownPaymentDisabled = isScheduleExtensionForDownPaymentDisabled;
|
||||
this.loanScheduleType = loanScheduleType;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -600,7 +606,7 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
public PrincipalInterest calculateTotalInterestForPeriod(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
|
||||
final BigDecimal interestCalculationGraceOnRepaymentPeriodFraction, final int periodNumber, final MathContext mc,
|
||||
final Money cumulatingInterestPaymentDueToGrace, final Money outstandingBalance, final LocalDate periodStartDate,
|
||||
final LocalDate periodEndDate) {
|
||||
|
||||
@@ -653,8 +659,6 @@ public final class LoanApplicationTerms {
|
||||
interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace.zero();
|
||||
} else if (isInterestFreeGracePeriod(periodNumber)) {
|
||||
interestForInstallment = interestForInstallment.zero();
|
||||
} else if (isInterestFreeGracePeriodFromDate(interestCalculationGraceOnRepaymentPeriodFraction)) {
|
||||
interestForInstallment = interestForThisInstallmentAfterGrace;
|
||||
} else {
|
||||
interestBroughtForwardDueToGrace = interestBroughtForwardDueToGrace.plus(interestForThisInstallmentBeforeGrace);
|
||||
}
|
||||
@@ -1185,7 +1189,7 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
|
||||
private Money calculateDecliningInterestDueForInstallmentAfterApplyingGrace(final PaymentPeriodsInOneYearCalculator calculator,
|
||||
final double interestCalculationGraceOnRepaymentPeriodFraction, final MathContext mc, final Money outstandingBalance,
|
||||
final BigDecimal interestCalculationGraceOnRepaymentPeriodFraction, final MathContext mc, final Money outstandingBalance,
|
||||
final int periodNumber, LocalDate periodStartDate, LocalDate periodEndDate) {
|
||||
|
||||
Money interest = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace(calculator, mc, outstandingBalance, periodStartDate,
|
||||
@@ -1195,30 +1199,30 @@ public final class LoanApplicationTerms {
|
||||
interest = interest.zero();
|
||||
}
|
||||
|
||||
Double fraction = interestCalculationGraceOnRepaymentPeriodFraction;
|
||||
BigDecimal fraction = interestCalculationGraceOnRepaymentPeriodFraction;
|
||||
|
||||
if (isInterestFreeGracePeriod(periodNumber)) {
|
||||
interest = interest.zero();
|
||||
} else if (isInterestFreeGracePeriodFromDate(interestCalculationGraceOnRepaymentPeriodFraction)) {
|
||||
|
||||
if (interestCalculationGraceOnRepaymentPeriodFraction >= Integer.valueOf(1).doubleValue()) {
|
||||
if (interestCalculationGraceOnRepaymentPeriodFraction.compareTo(BigDecimal.ZERO) > 0) {
|
||||
interest = interest.zero();
|
||||
fraction = fraction - Integer.valueOf(1).doubleValue();
|
||||
fraction = fraction.subtract(BigDecimal.ONE);
|
||||
|
||||
} else if (interestCalculationGraceOnRepaymentPeriodFraction > Double.parseDouble("0.25")
|
||||
&& interestCalculationGraceOnRepaymentPeriodFraction < Integer.valueOf(1).doubleValue()) {
|
||||
} else if (interestCalculationGraceOnRepaymentPeriodFraction.compareTo(BigDecimal.valueOf(0.25)) > 0
|
||||
&& interestCalculationGraceOnRepaymentPeriodFraction.compareTo(BigDecimal.ONE) < 0) {
|
||||
|
||||
final Money graceOnInterestForRepaymentPeriod = interest.multipliedBy(interestCalculationGraceOnRepaymentPeriodFraction);
|
||||
interest = interest.minus(graceOnInterestForRepaymentPeriod);
|
||||
fraction = Double.valueOf("0");
|
||||
fraction = BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
return interest;
|
||||
}
|
||||
|
||||
private boolean isInterestFreeGracePeriodFromDate(final double interestCalculationGraceOnRepaymentPeriodFraction) {
|
||||
return this.interestChargedFromDate != null && interestCalculationGraceOnRepaymentPeriodFraction > Double.parseDouble("0.0");
|
||||
private boolean isInterestFreeGracePeriodFromDate(BigDecimal interestCalculationGraceOnRepaymentPeriodFraction) {
|
||||
return this.interestChargedFromDate != null && interestCalculationGraceOnRepaymentPeriodFraction.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
|
||||
private Money calculateEqualPrincipalDueForInstallment(final MathContext mc, final int periodNumber) {
|
||||
@@ -1291,7 +1295,7 @@ public final class LoanApplicationTerms {
|
||||
this.graceOnArrearsAgeing, this.daysInMonthType.getValue(), this.daysInYearType.getValue(),
|
||||
this.interestRecalculationEnabled, this.isEqualAmortization, this.isDownPaymentEnabled,
|
||||
this.disbursedAmountPercentageForDownPayment, this.isAutoRepaymentForDownPaymentEnabled,
|
||||
this.isScheduleExtensionForDownPaymentDisabled);
|
||||
this.isScheduleExtensionForDownPaymentDisabled, this.loanScheduleType);
|
||||
}
|
||||
|
||||
public Integer getLoanTermFrequency() {
|
||||
@@ -1322,6 +1326,10 @@ public final class LoanApplicationTerms {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
public void setDisbursedPrincipal(Money disbursedPrincipal) {
|
||||
this.disbursedPrincipal = disbursedPrincipal;
|
||||
}
|
||||
|
||||
public LocalDate getInterestChargedFromLocalDate() {
|
||||
return this.interestChargedFromDate;
|
||||
}
|
||||
@@ -1781,4 +1789,8 @@ public final class LoanApplicationTerms {
|
||||
public Integer getInstallmentAmountInMultiplesOf() {
|
||||
return installmentAmountInMultiplesOf;
|
||||
}
|
||||
|
||||
public LoanScheduleType getLoanScheduleType() {
|
||||
return loanScheduleType;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,5 +22,5 @@ import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
public interface LoanScheduleGeneratorFactory {
|
||||
|
||||
LoanScheduleGenerator create(InterestMethod interestMethod);
|
||||
LoanScheduleGenerator create(LoanScheduleType loanScheduleType, InterestMethod interestMethod);
|
||||
}
|
||||
|
||||
+8
-8
@@ -20,7 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.organisation.monetary.data.CurrencyData;
|
||||
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
@@ -32,7 +32,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP
|
||||
*/
|
||||
public final class LoanScheduleModel {
|
||||
|
||||
private final Collection<LoanScheduleModelPeriod> periods;
|
||||
private final List<LoanScheduleModelPeriod> periods;
|
||||
private final ApplicationCurrency applicationCurrency;
|
||||
private final int loanTermInDays;
|
||||
private final Money totalPrincipalDisbursed;
|
||||
@@ -44,7 +44,7 @@ public final class LoanScheduleModel {
|
||||
private final BigDecimal totalRepaymentExpected;
|
||||
private final BigDecimal totalOutstanding;
|
||||
|
||||
public static LoanScheduleModel from(final Collection<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
||||
public static LoanScheduleModel from(final List<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||
final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
|
||||
@@ -54,7 +54,7 @@ public final class LoanScheduleModel {
|
||||
totalOutstanding);
|
||||
}
|
||||
|
||||
public static LoanScheduleModel withOverdueChargeUpdation(final Collection<LoanScheduleModelPeriod> periods,
|
||||
public static LoanScheduleModel withOverdueChargeUpdation(final List<LoanScheduleModelPeriod> periods,
|
||||
final LoanScheduleModel loanScheduleModel, final BigDecimal totalPenaltyChargesCharged) {
|
||||
|
||||
return new LoanScheduleModel(periods, loanScheduleModel.applicationCurrency, loanScheduleModel.loanTermInDays,
|
||||
@@ -63,7 +63,7 @@ public final class LoanScheduleModel {
|
||||
loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding);
|
||||
}
|
||||
|
||||
public static LoanScheduleModel withLoanScheduleModelPeriods(final Collection<LoanScheduleModelPeriod> periods,
|
||||
public static LoanScheduleModel withLoanScheduleModelPeriods(final List<LoanScheduleModelPeriod> periods,
|
||||
final LoanScheduleModel loanScheduleModel) {
|
||||
|
||||
return new LoanScheduleModel(periods, loanScheduleModel.applicationCurrency, loanScheduleModel.loanTermInDays,
|
||||
@@ -72,7 +72,7 @@ public final class LoanScheduleModel {
|
||||
loanScheduleModel.totalPenaltyChargesCharged, loanScheduleModel.totalRepaymentExpected, loanScheduleModel.totalOutstanding);
|
||||
}
|
||||
|
||||
private LoanScheduleModel(final Collection<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
||||
private LoanScheduleModel(final List<LoanScheduleModelPeriod> periods, final ApplicationCurrency applicationCurrency,
|
||||
final int loanTermInDays, final Money principalDisbursed, final BigDecimal totalPrincipalExpected,
|
||||
final BigDecimal totalPrincipalPaid, final BigDecimal totalInterestCharged, final BigDecimal totalFeeChargesCharged,
|
||||
final BigDecimal totalPenaltyChargesCharged, final BigDecimal totalRepaymentExpected, final BigDecimal totalOutstanding) {
|
||||
@@ -97,7 +97,7 @@ public final class LoanScheduleModel {
|
||||
|
||||
final BigDecimal totalCredits = BigDecimal.ZERO;
|
||||
|
||||
final Collection<LoanSchedulePeriodData> periodsData = new ArrayList<>();
|
||||
final List<LoanSchedulePeriodData> periodsData = new ArrayList<>();
|
||||
for (final LoanScheduleModelPeriod modelPeriod : this.periods) {
|
||||
periodsData.add(modelPeriod.toData());
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public final class LoanScheduleModel {
|
||||
totalPaidInAdvance, totalPaidLate, this.totalOutstanding, totalCredits);
|
||||
}
|
||||
|
||||
public Collection<LoanScheduleModelPeriod> getPeriods() {
|
||||
public List<LoanScheduleModelPeriod> getPeriods() {
|
||||
return this.periods;
|
||||
}
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
public enum LoanScheduleType {
|
||||
CUMULATIVE, PROGRESSIVE
|
||||
}
|
||||
+4
-2
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.time.LocalDate;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
|
||||
@@ -25,7 +27,7 @@ public interface PaymentPeriodsInOneYearCalculator {
|
||||
|
||||
Integer calculate(PeriodFrequencyType repaymentFrequencyType);
|
||||
|
||||
double calculatePortionOfRepaymentPeriodInterestChargingGrace(LocalDate repaymentPeriodStartDate, LocalDate scheduledDueDate,
|
||||
LocalDate interestChargedFromLocalDate, PeriodFrequencyType repaymentPeriodFrequencyType, Integer repaidEvery);
|
||||
BigDecimal calculatePortionOfRepaymentPeriodInterestChargingGrace(LocalDate repaymentPeriodStartDate, LocalDate scheduledDueDate,
|
||||
LocalDate interestChargedFromLocalDate, PeriodFrequencyType repaymentPeriodFrequencyType, int repaidEvery, MathContext mc);
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -156,4 +156,7 @@ public interface LoanProductConstants {
|
||||
|
||||
String ENABLE_INSTALLMENT_LEVEL_DELINQUENCY = "enableInstallmentLevelDelinquency";
|
||||
|
||||
// loan schedule type
|
||||
String LOAN_SCHEDULE_TYPE = "loanScheduleType";
|
||||
|
||||
}
|
||||
|
||||
+13
-3
@@ -62,6 +62,7 @@ import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.apache.fineract.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.apache.fineract.portfolio.fund.domain.Fund;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.apache.fineract.portfolio.loanproduct.exception.LoanProductGeneralRuleException;
|
||||
import org.apache.fineract.portfolio.rate.domain.Rate;
|
||||
@@ -303,6 +304,14 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
final LocalDate closeDate = command.localDateValueOfParameterNamed("closeDate");
|
||||
final ExternalId externalId = ExternalIdFactory.produce(command.stringValueOfParameterNamedAllowingNull("externalId"));
|
||||
|
||||
final LoanScheduleType loanScheduleType;
|
||||
if (command.hasParameter("loanScheduleType")) {
|
||||
loanScheduleType = LoanScheduleType.valueOf(command.stringValueOfParameterNamed("loanScheduleType"));
|
||||
} else {
|
||||
// For backward compatibility
|
||||
loanScheduleType = LoanScheduleType.CUMULATIVE;
|
||||
}
|
||||
|
||||
final boolean useBorrowerCycle = command
|
||||
.booleanPrimitiveValueOfParameterNamed(LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME);
|
||||
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations = new HashSet<>();
|
||||
@@ -427,7 +436,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
disallowExpectedDisbursements, allowApprovedDisbursedAmountsOverApplied, overAppliedCalculationType, overAppliedNumber,
|
||||
dueDaysForRepaymentEvent, overDueDaysForRepaymentEvent, enableDownPayment, disbursedAmountPercentageDownPayment,
|
||||
enableAutoRepaymentForDownPayment, repaymentStartDateType, disableScheduleExtensionForDownPayment,
|
||||
enableInstallmentLevelDelinquency);
|
||||
enableInstallmentLevelDelinquency, loanScheduleType);
|
||||
|
||||
}
|
||||
|
||||
@@ -642,7 +651,8 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
final Integer overAppliedNumber, final Integer dueDaysForRepaymentEvent, final Integer overDueDaysForRepaymentEvent,
|
||||
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||
final boolean enableAutoRepaymentForDownPayment, final RepaymentStartDateType repaymentStartDateType,
|
||||
final boolean disableScheduleExtensionForDownPayment, final boolean enableInstallmentLevelDelinquency) {
|
||||
final boolean disableScheduleExtensionForDownPayment, final boolean enableInstallmentLevelDelinquency,
|
||||
final LoanScheduleType loanScheduleType) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategyCode = transactionProcessingStrategyCode;
|
||||
|
||||
@@ -684,7 +694,7 @@ public class LoanProduct extends AbstractPersistableCustom {
|
||||
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
|
||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType.getValue(), daysInYearType.getValue(),
|
||||
isInterestRecalculationEnabled, isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment,
|
||||
enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment);
|
||||
enableAutoRepaymentForDownPayment, disableScheduleExtensionForDownPayment, loanScheduleType);
|
||||
|
||||
this.loanProductRelatedDetail.validateRepaymentPeriodWithGraceSettings();
|
||||
|
||||
|
||||
+14
-3
@@ -39,6 +39,7 @@ import org.apache.fineract.portfolio.common.domain.DaysInYearType;
|
||||
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
|
||||
import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
|
||||
|
||||
/**
|
||||
@@ -141,6 +142,10 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
@Column(name = "disable_schedule_extension_for_down_payment", nullable = false)
|
||||
private boolean disableScheduleExtensionForDownPayment;
|
||||
|
||||
@Column(name = "loan_schedule_type", nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
private LoanScheduleType loanScheduleType;
|
||||
|
||||
public static LoanProductRelatedDetail createFrom(final MonetaryCurrency currency, final BigDecimal principal,
|
||||
final BigDecimal nominalInterestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType,
|
||||
final BigDecimal nominalAnnualInterestRate, final InterestMethod interestMethod,
|
||||
@@ -151,7 +156,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing, final Integer daysInMonthType,
|
||||
final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization,
|
||||
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||
final boolean enableAutoRepaymentForDownPayment, final boolean disableScheduleExtensionForDownPayment) {
|
||||
final boolean enableAutoRepaymentForDownPayment, final boolean disableScheduleExtensionForDownPayment,
|
||||
final LoanScheduleType loanScheduleType) {
|
||||
|
||||
return new LoanProductRelatedDetail(currency, principal, nominalInterestRatePerPeriod, interestRatePeriodFrequencyType,
|
||||
nominalAnnualInterestRate, interestMethod, interestCalculationPeriodMethod, allowPartialPeriodInterestCalcualtion,
|
||||
@@ -159,7 +165,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
recurringMoratoriumOnPrincipalPeriods, graceOnInterestPayment, graceOnInterestCharged, amortizationMethod,
|
||||
inArrearsTolerance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
isEqualAmortization, enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment,
|
||||
disableScheduleExtensionForDownPayment);
|
||||
disableScheduleExtensionForDownPayment, loanScheduleType);
|
||||
}
|
||||
|
||||
protected LoanProductRelatedDetail() {
|
||||
@@ -176,7 +182,8 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
final BigDecimal inArrearsTolerance, final Integer graceOnArrearsAgeing, final Integer daysInMonthType,
|
||||
final Integer daysInYearType, final boolean isInterestRecalculationEnabled, final boolean isEqualAmortization,
|
||||
final boolean enableDownPayment, final BigDecimal disbursedAmountPercentageForDownPayment,
|
||||
final boolean enableAutoRepaymentForDownPayment, final boolean disableScheduleExtensionForDownPayment) {
|
||||
final boolean enableAutoRepaymentForDownPayment, final boolean disableScheduleExtensionForDownPayment,
|
||||
final LoanScheduleType loanScheduleType) {
|
||||
this.currency = currency;
|
||||
this.principal = defaultPrincipal;
|
||||
this.nominalInterestRatePerPeriod = defaultNominalInterestRatePerPeriod;
|
||||
@@ -207,6 +214,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.disbursedAmountPercentageForDownPayment = disbursedAmountPercentageForDownPayment;
|
||||
this.enableAutoRepaymentForDownPayment = enableAutoRepaymentForDownPayment;
|
||||
this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment;
|
||||
this.loanScheduleType = loanScheduleType;
|
||||
}
|
||||
|
||||
private Integer defaultToNullIfZero(final Integer value) {
|
||||
@@ -729,4 +737,7 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
this.disableScheduleExtensionForDownPayment = disableScheduleExtensionForDownPayment;
|
||||
}
|
||||
|
||||
public LoanScheduleType getLoanScheduleType() {
|
||||
return loanScheduleType;
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -32,4 +32,5 @@
|
||||
<include relativeToChangelogFile="true" file="parts/1007_add_loan_product_schedule_extension_for_down_payment_configuration.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/1008_add_loan_installment_delinquency_tag.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/1009_refactor_loan_Installment_delinquency_tag.xml"/>
|
||||
<include relativeToChangelogFile="true" file="parts/1010_introduce_loan_schedule_type_configuration.xml"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<?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
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
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"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_product_loan">
|
||||
<column defaultValue="CUMULATIVE" type="varchar(20)" name="loan_schedule_type">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<addColumn tableName="m_loan">
|
||||
<column defaultValue="CUMULATIVE" type="varchar(20)" name="loan_schedule_type">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Reference in New Issue
Block a user