MIFOSX-1918 : added Pre-closure interest calculation strategy
This commit is contained in:
+21
-15
@@ -2375,13 +2375,15 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
.getInterestRecalculationCompoundingMethod());
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(),
|
||||
loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(),
|
||||
getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(),
|
||||
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
||||
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod);
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms
|
||||
.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(), loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
||||
dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(),
|
||||
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
||||
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy());
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
@@ -4729,7 +4731,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance,
|
||||
loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails,
|
||||
calendarInstanceForInterestRecalculation, recalculationFrequencyType,
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf());
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
this.loanProduct.preCloseInterestCalculationStrategy());
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
@@ -4783,7 +4786,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount,
|
||||
disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod);
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy());
|
||||
|
||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
||||
DateUtils.getLocalDateOfTenant(), loanApplicationTerms, mc, charges(), holidayDetailDTO);
|
||||
@@ -4992,12 +4996,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
.getInterestRecalculationCompoundingMethod());
|
||||
}
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
||||
dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
|
||||
maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate,
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod);
|
||||
return LoanApplicationTerms
|
||||
.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType,
|
||||
expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount,
|
||||
disbursementData, maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate,
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+285
-140
@@ -144,6 +144,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// interest calculation
|
||||
final Map<LocalDate, Money> latePaymentMap = new HashMap<>();
|
||||
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
|
||||
LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||
while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) {
|
||||
|
||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||
@@ -202,126 +203,213 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// transaction processor configuration
|
||||
// will be added only if there is a loan repayment between the
|
||||
// period for interest first repayment strategies
|
||||
if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && loanRepaymentScheduleTransactionProcessor != null
|
||||
&& loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) {
|
||||
Money earlyPaidAmount = Money.zero(currency);
|
||||
LoanScheduleModelPeriod lastInstallment = null;
|
||||
if (isInterestRecalculationRequired(loanApplicationTerms, transactions)) {
|
||||
boolean checkForOutstanding = true;
|
||||
List<RecalculationDetail> unprocessedTransactions = new ArrayList<>();
|
||||
LoanScheduleModelPeriod installment = null;
|
||||
for (RecalculationDetail detail : applicableTransactions) {
|
||||
if (detail.isProcessed()) {
|
||||
continue;
|
||||
}
|
||||
if (detail.getTransactionDate().isBefore(scheduledDueDate)) {
|
||||
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
|
||||
if (!detail.getTransactionDate().isEqual(periodStartDate)) {
|
||||
int periodDays = Days.daysBetween(periodStartDate, detail.getTransactionDate()).getDays();
|
||||
// calculates period start date for interest
|
||||
// calculation as per the configuration
|
||||
periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms,
|
||||
periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest);
|
||||
if (loanRepaymentScheduleTransactionProcessor != null
|
||||
&& loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) {
|
||||
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
|
||||
if (!detail.getTransactionDate().isEqual(periodStartDate)) {
|
||||
int periodDays = Days.daysBetween(periodStartDate, detail.getTransactionDate()).getDays();
|
||||
// calculates period start date for interest
|
||||
// calculation as per the configuration
|
||||
periodStartDateApplicableForInterest = calculateInterestStartDateForPeriod(loanApplicationTerms,
|
||||
periodStartDate, idealDisbursementDate, periodStartDateApplicableForInterest);
|
||||
|
||||
int daysInPeriodApplicable = Days
|
||||
.daysBetween(periodStartDateApplicableForInterest, detail.getTransactionDate()).getDays();
|
||||
Money interestForThisinstallment = Money.zero(currency);
|
||||
if (daysInPeriodApplicable > 0) {
|
||||
// 5 determine interest till the transaction
|
||||
// date
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||
totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest,
|
||||
totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc,
|
||||
mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap),
|
||||
periodStartDateApplicableForInterest, detail.getTransactionDate(),
|
||||
daysInPeriodApplicableForInterest);
|
||||
interestForThisinstallment = principalInterestForThisPeriod.interest();
|
||||
int daysInPeriodApplicable = Days.daysBetween(periodStartDateApplicableForInterest,
|
||||
detail.getTransactionDate()).getDays();
|
||||
Money interestForThisinstallment = Money.zero(currency);
|
||||
if (daysInPeriodApplicable > 0) {
|
||||
// 5 determine interest till the transaction
|
||||
// date
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||
totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest,
|
||||
totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc,
|
||||
mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap),
|
||||
periodStartDateApplicableForInterest, detail.getTransactionDate(),
|
||||
daysInPeriodApplicableForInterest);
|
||||
interestForThisinstallment = principalInterestForThisPeriod.interest();
|
||||
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
|
||||
}
|
||||
|
||||
Money principalForThisPeriod = principalDisbursed.zero();
|
||||
|
||||
// applies all the applicable charges to the
|
||||
// newly
|
||||
// created installment
|
||||
PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod,
|
||||
interestForThisinstallment, null);
|
||||
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate,
|
||||
detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||
totalCumulativeInterest, numberOfRepayments, true, lastTransactionDate);
|
||||
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate,
|
||||
detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||
totalCumulativeInterest, numberOfRepayments, true, lastTransactionDate);
|
||||
|
||||
// 8. sum up real totalInstallmentDue from
|
||||
// components
|
||||
final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment)
|
||||
.plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
|
||||
// 9. create repayment period from parts
|
||||
installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate,
|
||||
detail.getTransactionDate(), principalForThisPeriod, outstandingBalance,
|
||||
interestForThisinstallment, feeChargesForInstallment, penaltyChargesForInstallment,
|
||||
totalInstallmentDue, true);
|
||||
periods.add(installment);
|
||||
|
||||
// update outstanding balance for interest
|
||||
// calculation as per the rest
|
||||
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap,
|
||||
detail.getTransactionDate(), outstandingBalanceAsPerRest, false);
|
||||
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap,
|
||||
detail.getTransactionDate(), outstandingBalanceAsPerRest, true);
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += periodDays;
|
||||
totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
|
||||
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
||||
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
||||
|
||||
periodStartDate = detail.getTransactionDate();
|
||||
periodStartDateApplicableForInterest = periodStartDate;
|
||||
instalmentNumber++;
|
||||
// creates and insert Loan repayment schedule
|
||||
// for
|
||||
// the period
|
||||
addLoanRepaymentScheduleInstallment(installments, installment);
|
||||
} else if (installment == null) {
|
||||
installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);
|
||||
}
|
||||
// applies the transaction as per transaction
|
||||
// strategy
|
||||
// on scheduled installments to identify the
|
||||
// unprocessed(early payment ) amounts
|
||||
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
|
||||
currency, installments);
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
|
||||
if (loanApplicationTerms.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
||||
LocalDate applicableDate = getNextRestScheduleDate(detail.getTransactionDate().minusDays(1),
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
checkForOutstanding = detail.getTransactionDate().isEqual(applicableDate);
|
||||
|
||||
}
|
||||
// reduces actual outstanding balance
|
||||
outstandingBalance = outstandingBalance.minus(unprocessed);
|
||||
// if outstanding balance becomes less than zero
|
||||
// then adjusts the princiapal
|
||||
Money addToPrinciapal = Money.zero(currency);
|
||||
if (!outstandingBalance.isGreaterThanZero()) {
|
||||
addToPrinciapal = addToPrinciapal.plus(outstandingBalance);
|
||||
outstandingBalance = outstandingBalance.zero();
|
||||
lastInstallment = installment;
|
||||
}
|
||||
// updates principal portion map with the early
|
||||
// payment amounts and applicable date as per
|
||||
// rest
|
||||
updatePrincipalPaidPortionToMap(loanApplicationTerms, holidayDetailDTO, principalPortionMap, installment,
|
||||
detail, unprocessed.plus(addToPrinciapal), installments);
|
||||
totalRepaymentExpected = totalRepaymentExpected.add(unprocessed.plus(addToPrinciapal).getAmount());
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal));
|
||||
|
||||
// method applies early payment strategy
|
||||
reducePrincipal = reducePrincipal.plus(unprocessed);
|
||||
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
|
||||
}
|
||||
// identify late payments and add compounding
|
||||
// details to
|
||||
// map for interest calculation
|
||||
if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(currentDate)) {
|
||||
latePaymentMap.clear();
|
||||
} else {
|
||||
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate,
|
||||
installments, true);
|
||||
}
|
||||
} else if (loanRepaymentScheduleTransactionProcessor != null) {
|
||||
LocalDate applicableDate = getNextRestScheduleDate(detail.getTransactionDate().minusDays(1),
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
if (applicableDate.isBefore(scheduledDueDate)) {
|
||||
List<LoanTransaction> currentTransactions = createCurrentTransactionList(detail);
|
||||
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
|
||||
currency, installments);
|
||||
Money arrears = detail.getTransaction().getAmount(currency);
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
arrears = getTotalAmount(latePaymentMap, currency);
|
||||
updateMapWithAmount(principalPortionMap, unprocessed, applicableDate);
|
||||
earlyPaidAmount = earlyPaidAmount.plus(unprocessed);
|
||||
|
||||
// this check is to identify pre-closure and
|
||||
// apply interest calculation as per
|
||||
// configuration
|
||||
if (!outstandingBalance.isGreaterThan(unprocessed)
|
||||
&& !loanApplicationTerms.getPreCloseInterestCalculationStrategy()
|
||||
.calculateTillRestFrequencyEnabled()) {
|
||||
|
||||
LocalDate calculateTill = detail.getTransactionDate();
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||
totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest,
|
||||
totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||
outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc,
|
||||
mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap),
|
||||
periodStartDateApplicableForInterest, calculateTill, daysInPeriodApplicableForInterest);
|
||||
if (!principalInterestForThisPeriod.interest()
|
||||
.plus(principalInterestForThisPeriod.interestPaymentDueToGrace()).plus(outstandingBalance)
|
||||
.isGreaterThan(unprocessed)) {
|
||||
earlyPaidAmount = earlyPaidAmount.minus(unprocessed);
|
||||
updateMapWithAmount(principalPortionMap, unprocessed.negated(), applicableDate);
|
||||
LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null,
|
||||
detail.getTransactionDate(), null, DateUtils.getLocalDateTimeOfTenant(), null);
|
||||
RecalculationDetail recalculationDetail = new RecalculationDetail(detail.getTransactionDate(),
|
||||
loanTransaction);
|
||||
unprocessedTransactions.add(recalculationDetail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
LoanTransaction loanTransaction = LoanTransaction.repayment(null, unprocessed, null, scheduledDueDate,
|
||||
null, DateUtils.getLocalDateTimeOfTenant(), null);
|
||||
RecalculationDetail recalculationDetail = new RecalculationDetail(scheduledDueDate, loanTransaction);
|
||||
unprocessedTransactions.add(recalculationDetail);
|
||||
checkForOutstanding = false;
|
||||
|
||||
outstandingBalance = outstandingBalance.minus(unprocessed);
|
||||
// if outstanding balance becomes less than
|
||||
// zero
|
||||
// then adjusts the princiapal
|
||||
Money addToPrinciapal = Money.zero(currency);
|
||||
if (outstandingBalance.isLessThanZero()) {
|
||||
addToPrinciapal = addToPrinciapal.plus(outstandingBalance);
|
||||
outstandingBalance = outstandingBalance.zero();
|
||||
updateMapWithAmount(principalPortionMap, addToPrinciapal, applicableDate);
|
||||
earlyPaidAmount = earlyPaidAmount.plus(addToPrinciapal);
|
||||
}
|
||||
|
||||
}
|
||||
if (arrears.isGreaterThanZero() && applicableDate.isBefore(currentDate)) {
|
||||
updateMapWithAmount(latePaymentMap, arrears.negated(), applicableDate);
|
||||
updateMapWithAmount(latePaymentMap, arrears, currentDate);
|
||||
}
|
||||
}
|
||||
|
||||
Money principalForThisPeriod = principalDisbursed.zero();
|
||||
|
||||
// applies all the applicable charges to the newly
|
||||
// created installment
|
||||
PrincipalInterest principalInterest = new PrincipalInterest(principalForThisPeriod, interestForThisinstallment,
|
||||
null);
|
||||
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(periodStartDate, detail.getTransactionDate(),
|
||||
loanCharges, currency, principalInterest, principalDisbursed, totalCumulativeInterest,
|
||||
numberOfRepayments, true, lastTransactionDate);
|
||||
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(periodStartDate,
|
||||
detail.getTransactionDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||
totalCumulativeInterest, numberOfRepayments, true, lastTransactionDate);
|
||||
|
||||
// 8. sum up real totalInstallmentDue from
|
||||
// components
|
||||
final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment)
|
||||
.plus(feeChargesForInstallment).plus(penaltyChargesForInstallment);
|
||||
// 9. create repayment period from parts
|
||||
installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate,
|
||||
detail.getTransactionDate(), principalForThisPeriod, outstandingBalance, interestForThisinstallment,
|
||||
feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue, true);
|
||||
periods.add(installment);
|
||||
|
||||
// update outstanding balance for interest
|
||||
// calculation as per the rest
|
||||
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap,
|
||||
detail.getTransactionDate(), outstandingBalanceAsPerRest, false);
|
||||
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap,
|
||||
detail.getTransactionDate(), outstandingBalanceAsPerRest, true);
|
||||
|
||||
// handle cumulative fields
|
||||
loanTermInDays += periodDays;
|
||||
totalRepaymentExpected = totalRepaymentExpected.add(totalInstallmentDue.getAmount());
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
|
||||
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
||||
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
||||
|
||||
periodStartDate = detail.getTransactionDate();
|
||||
periodStartDateApplicableForInterest = periodStartDate;
|
||||
instalmentNumber++;
|
||||
// creates and insert Loan repayment schedule for
|
||||
// the period
|
||||
addLoanRepaymentScheduleInstallment(installments, installment);
|
||||
} else if (installment == null) {
|
||||
installment = ((List<LoanScheduleModelPeriod>) periods).get(periods.size() - 1);
|
||||
}
|
||||
// applies the transaction as per transaction strategy
|
||||
// on scheduled installments to identify the
|
||||
// unprocessed(early payment ) amounts
|
||||
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
|
||||
currency, installments);
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
// reduces actual outstanding balance
|
||||
outstandingBalance = outstandingBalance.minus(unprocessed);
|
||||
// if outstanding balance becomes less than zero
|
||||
// then adjusts the princiapal
|
||||
Money addToPrinciapal = Money.zero(currency);
|
||||
if (outstandingBalance.isLessThanZero()) {
|
||||
addToPrinciapal = addToPrinciapal.plus(outstandingBalance);
|
||||
outstandingBalance = outstandingBalance.zero();
|
||||
}
|
||||
// updates principal portion map with the early
|
||||
// payment amounts and applicable date as per rest
|
||||
updatePrincipalPaidPortionToMap(loanApplicationTerms, holidayDetailDTO, principalPortionMap, installment,
|
||||
detail, unprocessed.plus(addToPrinciapal), installments);
|
||||
totalRepaymentExpected = totalRepaymentExpected.add(unprocessed.plus(addToPrinciapal).getAmount());
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(unprocessed.plus(addToPrinciapal));
|
||||
|
||||
// method applies early payment strategy
|
||||
reducePrincipal = reducePrincipal.plus(unprocessed);
|
||||
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
|
||||
}
|
||||
// identify late payments and add compounding details to
|
||||
// map for interest calculation
|
||||
if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(DateUtils.getLocalDateOfTenant())) {
|
||||
latePaymentMap.clear();
|
||||
} else {
|
||||
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate,
|
||||
installments, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (outstandingBalance.isZero() && disburseDetailMap.isEmpty()) {
|
||||
applicableTransactions.addAll(unprocessedTransactions);
|
||||
if (checkForOutstanding && outstandingBalance.isZero() && disburseDetailMap.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -364,9 +452,19 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
principalForThisPeriod = principalForThisPeriod.zero();
|
||||
}
|
||||
|
||||
// earlyPaidAmount is already subtracted from balancereducePrincipal
|
||||
// reducePrincipal.plus(unprocessed);
|
||||
Money reducedBalance = earlyPaidAmount;
|
||||
earlyPaidAmount = earlyPaidAmount.minus(principalForThisPeriod);
|
||||
if (earlyPaidAmount.isGreaterThanZero()) {
|
||||
reducePrincipal = reducePrincipal.plus(earlyPaidAmount);
|
||||
reducePrincipal = applyEarlyPaymentStrategy(loanApplicationTerms, reducePrincipal);
|
||||
principalForThisPeriod = principalForThisPeriod.plus(earlyPaidAmount);
|
||||
}
|
||||
|
||||
// 6. update outstandingLoanBlance using current period
|
||||
// 'principalDue'
|
||||
outstandingBalance = outstandingBalance.minus(principalForThisPeriod);
|
||||
outstandingBalance = outstandingBalance.minus(principalForThisPeriod.minus(reducedBalance));
|
||||
|
||||
if (outstandingBalance.isLessThanZero()) {
|
||||
principalForThisPeriod = principalForThisPeriod.plus(outstandingBalance);
|
||||
@@ -386,6 +484,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
final Money totalInstallmentDue = principalForThisPeriod.plus(interestForThisinstallment).plus(feeChargesForInstallment)
|
||||
.plus(penaltyChargesForInstallment);
|
||||
|
||||
// if previous installment is last then add interest to same
|
||||
// installment
|
||||
if (lastInstallment != null && principalForThisPeriod.isZero()) {
|
||||
lastInstallment.addInterestAmount(interestForThisinstallment);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 9. create repayment period from parts
|
||||
LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate,
|
||||
scheduledDueDate, principalForThisPeriod, outstandingBalance, interestForThisinstallment, feeChargesForInstallment,
|
||||
@@ -393,9 +498,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
// apply loan transactions on installments to identify early/late
|
||||
// payments for interest recalculation
|
||||
if (isInterestRecalculationRequired(loanApplicationTerms, transactions)) {
|
||||
if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && loanRepaymentScheduleTransactionProcessor != null) {
|
||||
addLoanRepaymentScheduleInstallment(installments, installment);
|
||||
|
||||
for (RecalculationDetail detail : applicableTransactions) {
|
||||
if (!detail.isProcessed()) {
|
||||
List<LoanTransaction> currentTransactions = new ArrayList<>(2);
|
||||
@@ -405,18 +509,23 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// unprocessed(early payment ) amounts
|
||||
Money unprocessed = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(currentTransactions,
|
||||
currency, installments);
|
||||
|
||||
if (unprocessed.isGreaterThanZero()) {
|
||||
outstandingBalance = outstandingBalance.minus(unprocessed);
|
||||
// pre closure check and processing
|
||||
if (outstandingBalance.isLessThan(interestForThisinstallment)
|
||||
&& !scheduledDueDate.equals(detail.getTransactionDate())) {
|
||||
LocalDate calculateTill = detail.getTransactionDate();
|
||||
if (loanApplicationTerms.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
||||
calculateTill = getNextRestScheduleDate(calculateTill.minusDays(1), loanApplicationTerms,
|
||||
holidayDetailDTO);
|
||||
}
|
||||
PrincipalInterest interestTillDate = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
|
||||
totalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms,
|
||||
periodNumber, mc, mergeLateAndPaymentMaps(principalPortionMap, latePaymentMap, disburseDetailMap),
|
||||
periodStartDateApplicableForInterest, detail.getTransactionDate(),
|
||||
daysInPeriodApplicableForInterest);
|
||||
periodStartDateApplicableForInterest, calculateTill, daysInPeriodApplicableForInterest);
|
||||
Money diff = interestForThisinstallment.minus(interestTillDate.interest());
|
||||
if (!outstandingBalance.minus(diff).isGreaterThanZero()) {
|
||||
outstandingBalance = outstandingBalance.minus(diff);
|
||||
@@ -455,7 +564,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
// identify late payments and adds compounding details to map
|
||||
if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(DateUtils.getLocalDateOfTenant())) {
|
||||
if (!reducePrincipal.isZero() || !installment.periodDueDate().isBefore(currentDate)) {
|
||||
latePaymentMap.clear();
|
||||
} else {
|
||||
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, scheduledDueDate,
|
||||
@@ -472,7 +581,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
amountApplicableDate = getNextRestScheduleDate(installment.periodDueDate().minusDays(1), loanApplicationTerms,
|
||||
holidayDetailDTO);
|
||||
}
|
||||
updateMapWithAmount(principalPortionMap, principalForThisPeriod, amountApplicableDate);
|
||||
updateMapWithAmount(principalPortionMap, principalForThisPeriod.minus(reducedBalance), amountApplicableDate);
|
||||
|
||||
// update outstanding balance for interest calculation
|
||||
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, scheduledDueDate,
|
||||
@@ -515,7 +624,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
// this block is to add extra re-payment schedules with interest portion
|
||||
// if the loan not paid with in loan term
|
||||
LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||
|
||||
if (isInterestRecalculationRequired(loanApplicationTerms, transactions) && latePaymentMap.size() > 0
|
||||
&& currentDate.isAfter(periodStartDate)) {
|
||||
Money totalInterest = addInterestOnlyRepaymentScheduleForCurrentdate(mc, loanApplicationTerms, holidayDetailDTO, currency,
|
||||
@@ -743,9 +852,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
principalEffectiveDate = getNextRestScheduleDate(loanRepaymentScheduleInstallment.getDueDate().minusDays(1),
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
}
|
||||
updateMapWithAmount(latePaymentMap, loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency),
|
||||
principalEffectiveDate);
|
||||
totalCompoundingAmount = totalCompoundingAmount.plus(loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency));
|
||||
if (principalEffectiveDate.isBefore(DateUtils.getLocalDateOfTenant())) {
|
||||
updateMapWithAmount(latePaymentMap, loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency),
|
||||
principalEffectiveDate);
|
||||
totalCompoundingAmount = totalCompoundingAmount
|
||||
.plus(loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency));
|
||||
}
|
||||
totalCompoundingAmount = updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap,
|
||||
totalCompoundingAmount, loanRepaymentScheduleInstallment);
|
||||
}
|
||||
@@ -762,25 +874,27 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isCompoundingEnabled()) {
|
||||
LocalDate compoundingEffectiveDate = getNextCompoundScheduleDate(loanRepaymentScheduleInstallment.getDueDate().minusDays(1),
|
||||
loanApplicationTerms, holidayDetailDTO);
|
||||
Money amount = Money.zero(currency);
|
||||
switch (loanApplicationTerms.getInterestRecalculationCompoundingMethod()) {
|
||||
case INTEREST:
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
|
||||
break;
|
||||
case FEE:
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
break;
|
||||
case INTEREST_AND_FEE:
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (compoundingEffectiveDate.isBefore(DateUtils.getLocalDateOfTenant())) {
|
||||
Money amount = Money.zero(currency);
|
||||
switch (loanApplicationTerms.getInterestRecalculationCompoundingMethod()) {
|
||||
case INTEREST:
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
|
||||
break;
|
||||
case FEE:
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
break;
|
||||
case INTEREST_AND_FEE:
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getInterestOutstanding(currency));
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency));
|
||||
amount = amount.plus(loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
updateMapWithAmount(latePaymentMap, amount, compoundingEffectiveDate);
|
||||
totalCompoundingAmount = totalCompoundingAmount.plus(amount);
|
||||
}
|
||||
updateMapWithAmount(latePaymentMap, amount, compoundingEffectiveDate);
|
||||
totalCompoundingAmount = totalCompoundingAmount.plus(amount);
|
||||
}
|
||||
return totalCompoundingAmount;
|
||||
}
|
||||
@@ -852,6 +966,16 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
map.put(amountApplicableDate, principalPaid);
|
||||
}
|
||||
|
||||
private Money getTotalAmount(final Map<LocalDate, Money> map, final MonetaryCurrency currency) {
|
||||
Money total = Money.zero(currency);
|
||||
for (Map.Entry<LocalDate, Money> mapEntry : map.entrySet()) {
|
||||
if (mapEntry.getKey().isBefore(DateUtils.getLocalDateOfTenant())) {
|
||||
total = total.plus(mapEntry.getValue());
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO,
|
||||
@@ -1381,16 +1505,20 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Set<LoanCharge> charges, final HolidayDetailDTO holidayDetailDTO) {
|
||||
Money feeCharges = Money.zero(currency);
|
||||
Money penaltyCharges = Money.zero(currency);
|
||||
Money totalCompoundingAmount = Money.zero(currency);
|
||||
Money totalPrincipal = Money.zero(currency);
|
||||
Money totalInterest = Money.zero(currency);
|
||||
Money reducePrincipalAsperRest = Money.zero(currency);
|
||||
LocalDate reducePrincipalRestDate = onDate;
|
||||
LocalDate calculateInterestFrom = onDate;
|
||||
LocalDate periodStartDate = onDate;
|
||||
LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||
Money principalOutstanding = Money.zero(currency);
|
||||
Integer periodNumber = 1;
|
||||
// for multi disburse loan to identify all disbursed principal for
|
||||
// interest calculation
|
||||
Map<LocalDate, Money> differenceMap = new HashMap<>();
|
||||
Map<LocalDate, Money> disburseAmtMap = new HashMap<>();
|
||||
Map<LocalDate, Money> differenceMap = new TreeMap<>();
|
||||
Map<LocalDate, Money> disburseAmtMap = new TreeMap<>();
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) {
|
||||
if (disbursementData.isDisbursed()) {
|
||||
@@ -1412,10 +1540,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
feeCharges = feeCharges.plus(currentInstallment.getFeeChargesOutstanding(currency));
|
||||
penaltyCharges = penaltyCharges.plus(currentInstallment.getPenaltyChargesOutstanding(currency));
|
||||
principalOutstanding = principalOutstanding.minus(currentInstallment.getPrincipal(currency));
|
||||
LocalDate restDate = getNextRestScheduleDate(currentInstallment.getDueDate().minusDays(1), loanApplicationTerms,
|
||||
holidayDetailDTO);
|
||||
if (restDate.isAfter(currentDate)) {
|
||||
reducePrincipalRestDate = restDate;
|
||||
reducePrincipalAsperRest = reducePrincipalAsperRest.plus(currentInstallment.getPrincipalOutstanding(currency));
|
||||
}
|
||||
|
||||
updateMapWithAmount(differenceMap, currentInstallment.getPrincipalOutstanding(currency),
|
||||
currentInstallment.getDueDate());
|
||||
updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, currency, differenceMap, Money.zero(currency),
|
||||
currentInstallment);
|
||||
totalCompoundingAmount = updateMapWithCompoundingDetails(loanApplicationTerms, holidayDetailDTO, currency,
|
||||
differenceMap, totalCompoundingAmount, currentInstallment);
|
||||
} else {
|
||||
totalPrincipal = totalPrincipal.minus(currentInstallment.getPrincipalCompleted(currency));
|
||||
totalInterest = totalInterest.minus(currentInstallment.getInterestPaid(currency)).minus(
|
||||
@@ -1433,7 +1568,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
principalOutstanding = principalOutstanding.minus(currentInstallment.getPrincipal(currency));
|
||||
}
|
||||
}
|
||||
|
||||
Money arrears = totalPrincipal;
|
||||
totalPrincipal = totalPrincipal.plus(principalOutstanding);
|
||||
|
||||
for (Map.Entry<LocalDate, Money> disburseDetail : disburseAmtMap.entrySet()) {
|
||||
@@ -1467,12 +1602,22 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
if (calculateInterestFrom.isBefore(periodStartDateApplicableForInterest)) {
|
||||
calculateInterestFrom = periodStartDateApplicableForInterest;
|
||||
}
|
||||
LocalDate calculateTill = onDate;
|
||||
if (loanApplicationTerms.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
||||
LocalDate applicableDate = getNextRestScheduleDate(onDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
|
||||
calculateTill = applicableDate;
|
||||
}
|
||||
if (arrears.isGreaterThanZero()) {
|
||||
updateMapWithAmount(differenceMap, arrears.minus(reducePrincipalAsperRest).plus(totalCompoundingAmount).negated(),
|
||||
currentDate);
|
||||
updateMapWithAmount(differenceMap, reducePrincipalAsperRest.negated(), reducePrincipalRestDate);
|
||||
}
|
||||
if (calculateInterestFrom.isBefore(onDate)) {
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf(0);
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(),
|
||||
totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), principalOutstanding, loanApplicationTerms,
|
||||
periodNumber, mc, differenceMap, calculateInterestFrom, onDate, daysInPeriodApplicableForInterest);
|
||||
periodNumber, mc, differenceMap, calculateInterestFrom, calculateTill, daysInPeriodApplicableForInterest);
|
||||
interest = interest.plus(principalInterestForThisPeriod.interest());
|
||||
|
||||
}
|
||||
|
||||
+21
-8
@@ -31,6 +31,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRelatedDetail;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
@@ -137,6 +138,8 @@ public final class LoanApplicationTerms {
|
||||
private final BigDecimal principalThresholdForLastInstalment;
|
||||
private final Integer installmentAmountInMultiplesOf;
|
||||
|
||||
private final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
|
||||
@@ -151,7 +154,8 @@ public final class LoanApplicationTerms {
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final Integer graceOnArrearsAgeing,
|
||||
final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf) {
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||
@@ -162,7 +166,8 @@ public final class LoanApplicationTerms {
|
||||
graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount,
|
||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
||||
recalculationFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf);
|
||||
recalculationFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
preCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -174,7 +179,8 @@ public final class LoanApplicationTerms {
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance restCalendarInstance,
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod) {
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod,
|
||||
final LoanPreCloseInterestCalculationStrategy loanPreCloseInterestCalculationStrategy) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -207,7 +213,7 @@ public final class LoanApplicationTerms {
|
||||
multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType,
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf);
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -218,7 +224,7 @@ public final class LoanApplicationTerms {
|
||||
final List<LoanTermVariationsData> emiAmountVariations, final LocalDate interestChargedFromDate,
|
||||
final LoanInterestRecalculationDetails interestRecalculationDetails, final CalendarInstance restCalendarInstance,
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final BigDecimal principalThresholdForLastInstalment,
|
||||
Integer installmentAmountInMultiplesOf) {
|
||||
Integer installmentAmountInMultiplesOf, final LoanPreCloseInterestCalculationStrategy loanPreCloseInterestCalculationStrategy) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -256,7 +262,7 @@ public final class LoanApplicationTerms {
|
||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(),
|
||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
||||
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType,
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf);
|
||||
principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||
@@ -275,7 +281,7 @@ public final class LoanApplicationTerms {
|
||||
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
|
||||
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
|
||||
applicationTerms.recalculationFrequencyType, applicationTerms.principalThresholdForLastInstalment,
|
||||
applicationTerms.installmentAmountInMultiplesOf);
|
||||
applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -293,7 +299,8 @@ public final class LoanApplicationTerms {
|
||||
final boolean isInterestRecalculationEnabled, final LoanRescheduleStrategyMethod rescheduleStrategyMethod,
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod,
|
||||
final CalendarInstance restCalendarInstance, final RecalculationFrequencyType recalculationFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf) {
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy) {
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||
@@ -337,6 +344,7 @@ public final class LoanApplicationTerms {
|
||||
this.recalculationFrequencyType = recalculationFrequencyType;
|
||||
this.principalThresholdForLastInstalment = principalThresholdForLastInstalment;
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1170,4 +1178,9 @@ public final class LoanApplicationTerms {
|
||||
public InterestCalculationPeriodMethod getInterestCalculationPeriodMethod() {
|
||||
return this.interestCalculationPeriodMethod;
|
||||
}
|
||||
|
||||
|
||||
public LoanPreCloseInterestCalculationStrategy getPreCloseInterestCalculationStrategy() {
|
||||
return this.preCloseInterestCalculationStrategy;
|
||||
}
|
||||
}
|
||||
+5
@@ -104,4 +104,9 @@ public final class LoanScheduleModelDisbursementPeriod implements LoanScheduleMo
|
||||
public void addPrincipalAmount(@SuppressWarnings("unused") Money principalDue) {
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterestAmount(@SuppressWarnings("unused") Money principalDue) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
+2
@@ -36,4 +36,6 @@ public interface LoanScheduleModelPeriod {
|
||||
boolean isRecalculatedInterestComponent();
|
||||
|
||||
void addPrincipalAmount(Money principalDue);
|
||||
|
||||
void addInterestAmount(Money interestDue);
|
||||
}
|
||||
|
||||
+7
-1
@@ -22,7 +22,7 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
||||
private final LocalDate dueDate;
|
||||
private Money principalDue;
|
||||
private final Money outstandingLoanBalance;
|
||||
private final Money interestDue;
|
||||
private Money interestDue;
|
||||
private Money feeChargesDue;
|
||||
private Money penaltyChargesDue;
|
||||
private Money totalDue;
|
||||
@@ -135,4 +135,10 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
||||
public boolean isRecalculatedInterestComponent() {
|
||||
return this.recalculatedInterestComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterestAmount(Money interestDue) {
|
||||
this.interestDue = this.interestDue.plus(interestDue);
|
||||
this.totalDue = this.totalDue.plus(principalDue);
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -281,7 +281,8 @@ public class LoanScheduleAssembler {
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsToleranceMoney,
|
||||
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
|
||||
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType,
|
||||
restCalendarInstance, principalThresholdForLastInstalment, installmentAmountInMultiplesOf);
|
||||
restCalendarInstance, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanProduct.preCloseInterestCalculationStrategy());
|
||||
}
|
||||
|
||||
private CalendarInstance createInterestRecalculationCalendarInstance(final LocalDate calendarStartDate,
|
||||
|
||||
+7
-8
@@ -132,6 +132,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
|
||||
final Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
|
||||
LocalDate today = DateUtils.getLocalDateOfTenant();
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loanRepaymentScheduleTransactionProcessorFactory
|
||||
.determineProcessor(loan.transactionProcessingStrategy());
|
||||
|
||||
@@ -149,7 +150,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
final List<LoanSchedulePeriodData> futureInstallments = new ArrayList<>();
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : loan.fetchRepaymentScheduleInstallments()) {
|
||||
if (currentInstallment.isNotFullyPaidOff()) {
|
||||
if (!currentInstallment.getDueDate().isAfter(LocalDate.now())) {
|
||||
if (!currentInstallment.getDueDate().isAfter(today)) {
|
||||
totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency));
|
||||
}
|
||||
}
|
||||
@@ -164,8 +165,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance);
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(
|
||||
loan.fetchRepaymentScheduleInstallments(), currency, LocalDate.now(), loanApplicationTerms, loan.charges(),
|
||||
loan.getOfficeId());
|
||||
loan.fetchRepaymentScheduleInstallments(), currency, today, loanApplicationTerms, loan.charges(), loan.getOfficeId());
|
||||
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
Money interestDue = Money.zero(currency);
|
||||
@@ -180,18 +180,17 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
modifiedTransactions.add(LoanTransaction.copyTransactionProperties(loanTransaction));
|
||||
}
|
||||
if (isNewPaymentRequired) {
|
||||
LoanTransaction ondayPaymentTransaction = LoanTransaction.repayment(null, totalAmount, null, LocalDate.now(), null,
|
||||
LoanTransaction ondayPaymentTransaction = LoanTransaction.repayment(null, totalAmount, null, today, null,
|
||||
DateUtils.getLocalDateTimeOfTenant(), null);
|
||||
modifiedTransactions.add(ondayPaymentTransaction);
|
||||
}
|
||||
|
||||
LoanScheduleModel model = this.loanScheduleAssembler.assembleForInterestRecalculation(loanApplicationTerms, loan.getOfficeId(),
|
||||
modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, LocalDate.now());
|
||||
modifiedTransactions, loan.charges(), loanRepaymentScheduleTransactionProcessor, today);
|
||||
LoanScheduleData scheduleDate = model.toData();
|
||||
Collection<LoanSchedulePeriodData> periodDatas = scheduleDate.getPeriods();
|
||||
for (LoanSchedulePeriodData periodData : periodDatas) {
|
||||
if ((periodData.periodDueDate().isEqual(LocalDate.now()) || periodData.periodDueDate().isAfter(LocalDate.now()))
|
||||
&& isNewPaymentRequired) {
|
||||
if ((periodData.periodDueDate().isEqual(today) || periodData.periodDueDate().isAfter(today)) && isNewPaymentRequired) {
|
||||
LoanSchedulePeriodData loanSchedulePeriodData = LoanSchedulePeriodData.repaymentOnlyPeriod(periodData.periodNumber(),
|
||||
periodData.periodFromDate(), periodData.periodDueDate(), totalPrincipal.getAmount(), periodData
|
||||
.principalLoanBalanceOutstanding(), interestDue.getAmount(), loanRepaymentScheduleInstallment
|
||||
@@ -199,7 +198,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesCharged(currency).getAmount(), totalAmount.getAmount());
|
||||
futureInstallments.add(loanSchedulePeriodData);
|
||||
isNewPaymentRequired = false;
|
||||
} else if (periodData.periodDueDate().isAfter(LocalDate.now())) {
|
||||
} else if (periodData.periodDueDate().isAfter(today)) {
|
||||
futureInstallments.add(periodData);
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -75,6 +75,7 @@ public interface LoanProductConstants {
|
||||
public static final String recalculationRestFrequencyIntervalParameterName = "recalculationRestFrequencyInterval";
|
||||
public static final String recalculationRestFrequencyDateParamName = "recalculationRestFrequencyDate";
|
||||
public static final String isArrearsBasedOnOriginalScheduleParamName = "isArrearsBasedOnOriginalSchedule";
|
||||
public static final String preCloseInterestCalculationStrategyParamName = "preCloseInterestCalculationStrategy";
|
||||
|
||||
// Guarantee related
|
||||
public static final String holdGuaranteeFundsParamName = "holdGuaranteeFunds";
|
||||
|
||||
+4
-1
@@ -277,12 +277,15 @@ public class LoanProductsApiResource {
|
||||
final List<EnumOptionData> rescheduleStrategyTypeOptions = dropdownReadPlatformService.retrieveRescheduleStrategyTypeOptions();
|
||||
final List<EnumOptionData> interestRecalculationFrequencyTypeOptions = dropdownReadPlatformService
|
||||
.retrieveInterestRecalculationFrequencyTypeOptions();
|
||||
final List<EnumOptionData> preCloseInterestCalculationStrategyOptions = dropdownReadPlatformService
|
||||
.retrivePreCloseInterestCalculationStrategyOptions();
|
||||
|
||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions,
|
||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions,
|
||||
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions,
|
||||
accountingRuleTypeOptions, loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, daysInYearTypeOptions,
|
||||
interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions);
|
||||
interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions,
|
||||
preCloseInterestCalculationStrategyOptions);
|
||||
}
|
||||
|
||||
}
|
||||
+6
-1
@@ -129,6 +129,8 @@ public class LoanProductData {
|
||||
private final List<EnumOptionData> interestRecalculationCompoundingTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final List<EnumOptionData> rescheduleStrategyTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final List<EnumOptionData> preCloseInterestCalculationStrategyOptions;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final List<EnumOptionData> interestRecalculationFrequencyTypeOptions;
|
||||
@@ -479,6 +481,7 @@ public class LoanProductData {
|
||||
|
||||
this.canDefineInstallmentAmount = canDefineInstallmentAmount;
|
||||
this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf;
|
||||
this.preCloseInterestCalculationStrategyOptions = null;
|
||||
|
||||
}
|
||||
|
||||
@@ -491,7 +494,8 @@ public class LoanProductData {
|
||||
final Map<String, List<GLAccountData>> accountingMappingOptions, final List<EnumOptionData> accountingRuleOptions,
|
||||
final List<EnumOptionData> valueConditionTypeOptions, final List<EnumOptionData> daysInMonthTypeOptions,
|
||||
final List<EnumOptionData> daysInYearTypeOptions, final List<EnumOptionData> interestRecalculationCompoundingTypeOptions,
|
||||
final List<EnumOptionData> rescheduleStrategyTypeOptions, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions) {
|
||||
final List<EnumOptionData> rescheduleStrategyTypeOptions, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions,
|
||||
final List<EnumOptionData> preCloseInterestCalculationStrategyOptions) {
|
||||
this.id = productData.id;
|
||||
this.name = productData.name;
|
||||
this.shortName = productData.shortName;
|
||||
@@ -592,6 +596,7 @@ public class LoanProductData {
|
||||
|
||||
this.canDefineInstallmentAmount = productData.canDefineInstallmentAmount;
|
||||
this.installmentAmountInMultiplesOf = productData.installmentAmountInMultiplesOf;
|
||||
this.preCloseInterestCalculationStrategyOptions = preCloseInterestCalculationStrategyOptions;
|
||||
}
|
||||
|
||||
private Collection<ChargeData> nullIfEmpty(final Collection<ChargeData> charges) {
|
||||
|
||||
+9
-2
@@ -6,11 +6,13 @@
|
||||
package org.mifosplatform.portfolio.loanproduct.data;
|
||||
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestRecalculationCompoundingType;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.preCloseInterestCalculationStrategy;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
|
||||
|
||||
public class LoanProductInterestRecalculationData {
|
||||
@@ -26,11 +28,14 @@ public class LoanProductInterestRecalculationData {
|
||||
private final LocalDate recalculationRestFrequencyDate;
|
||||
@SuppressWarnings("unused")
|
||||
private final boolean isArrearsBasedOnOriginalSchedule;
|
||||
@SuppressWarnings("unused")
|
||||
private final EnumOptionData preCloseInterestCalculationStrategy;
|
||||
|
||||
public LoanProductInterestRecalculationData(final Long id, final Long productId,
|
||||
final EnumOptionData interestRecalculationCompoundingType, final EnumOptionData rescheduleStrategyType,
|
||||
final EnumOptionData recalculationRestFrequencyType, final Integer recalculationRestFrequencyInterval,
|
||||
final LocalDate recalculationRestFrequencyDate, boolean isArrearsBasedOnOriginalSchedule) {
|
||||
final LocalDate recalculationRestFrequencyDate, boolean isArrearsBasedOnOriginalSchedule,
|
||||
final EnumOptionData preCloseInterestCalculationStrategy) {
|
||||
this.id = id;
|
||||
this.productId = productId;
|
||||
this.interestRecalculationCompoundingType = interestRecalculationCompoundingType;
|
||||
@@ -39,6 +44,7 @@ public class LoanProductInterestRecalculationData {
|
||||
this.recalculationRestFrequencyInterval = recalculationRestFrequencyInterval;
|
||||
this.recalculationRestFrequencyDate = recalculationRestFrequencyDate;
|
||||
this.isArrearsBasedOnOriginalSchedule = isArrearsBasedOnOriginalSchedule;
|
||||
this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy;
|
||||
}
|
||||
|
||||
public static LoanProductInterestRecalculationData sensibleDefaultsForNewLoanProductCreation() {
|
||||
@@ -50,9 +56,10 @@ public class LoanProductInterestRecalculationData {
|
||||
final Integer recalculationRestFrequencyInterval = null;
|
||||
final LocalDate recalculationRestFrequencyDate = null;
|
||||
final boolean isArrearsBasedOnOriginalSchedule = false;
|
||||
final EnumOptionData preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE);
|
||||
return new LoanProductInterestRecalculationData(id, productId, interestRecalculationCompoundingType, rescheduleStrategyType,
|
||||
recalculationRestFrequencyType, recalculationRestFrequencyInterval, recalculationRestFrequencyDate,
|
||||
isArrearsBasedOnOriginalSchedule);
|
||||
isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
public EnumOptionData getInterestRecalculationCompoundingType() {
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum LoanPreCloseInterestCalculationStrategy {
|
||||
|
||||
NONE(0, "loanPreCloseInterestCalculationStrategy.none"), //
|
||||
TILL_PRE_CLOSURE_DATE(1, "loanPreCloseInterestCalculationStrategy.tillPreClosureDate"), //
|
||||
TILL_REST_FREQUENCY_DATE(2, "loanPreCloseInterestCalculationStrategy.tillRestFrequencyDate");
|
||||
|
||||
// REPAYMENT_PERIOD_DATE(3,
|
||||
// "loanPreCloseInterestCalculationStrategy.repaymentPeriodDate")
|
||||
|
||||
private Integer value;
|
||||
private String code;
|
||||
|
||||
private static final Map<Integer, LoanPreCloseInterestCalculationStrategy> intToEnumMap = new HashMap<>();
|
||||
private static int minValue;
|
||||
private static int maxValue;
|
||||
static {
|
||||
int i = 0;
|
||||
for (final LoanPreCloseInterestCalculationStrategy type : LoanPreCloseInterestCalculationStrategy.values()) {
|
||||
if (i == 0) {
|
||||
minValue = type.value;
|
||||
}
|
||||
intToEnumMap.put(type.value, type);
|
||||
if (minValue >= type.value) {
|
||||
minValue = type.value;
|
||||
}
|
||||
if (maxValue < type.value) {
|
||||
maxValue = type.value;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static LoanPreCloseInterestCalculationStrategy fromInt(final Integer ruleTypeValue) {
|
||||
final LoanPreCloseInterestCalculationStrategy type = intToEnumMap.get(ruleTypeValue);
|
||||
return type;
|
||||
}
|
||||
|
||||
private LoanPreCloseInterestCalculationStrategy(final Integer value, final String code) {
|
||||
this.value = value;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public static int getMinValue() {
|
||||
return minValue;
|
||||
}
|
||||
|
||||
public static int getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public boolean calculateTillRestFrequencyEnabled() {
|
||||
return this.getValue().equals(LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE.getValue());
|
||||
}
|
||||
|
||||
public boolean calculateTillPreClosureDateEnabled() {
|
||||
return this.getValue().equals(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue());
|
||||
}
|
||||
}
|
||||
+8
@@ -1069,4 +1069,12 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return this.installmentAmountInMultiplesOf;
|
||||
}
|
||||
|
||||
public LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy() {
|
||||
LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy = LoanPreCloseInterestCalculationStrategy.NONE;
|
||||
if (this.isInterestRecalculationEnabled()) {
|
||||
preCloseInterestCalculationStrategy = getProductInterestRecalculationDetails().preCloseInterestCalculationStrategy();
|
||||
}
|
||||
return preCloseInterestCalculationStrategy;
|
||||
}
|
||||
|
||||
}
|
||||
+28
-3
@@ -31,6 +31,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
@Table(name = "m_product_loan_recalculation_details")
|
||||
public class LoanProductInterestRecalculationDetails extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@OneToOne
|
||||
@JoinColumn(name = "product_id", nullable = false)
|
||||
private LoanProduct loanProduct;
|
||||
@@ -60,6 +61,9 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
|
||||
@Column(name = "arrears_based_on_original_schedule")
|
||||
private boolean isArrearsBasedOnOriginalSchedule;
|
||||
|
||||
@Column(name = "pre_close_interest_calculation_strategy")
|
||||
private Integer preCloseInterestCalculationStrategy;
|
||||
|
||||
protected LoanProductInterestRecalculationDetails() {
|
||||
//
|
||||
}
|
||||
@@ -91,19 +95,27 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
|
||||
recurrenceInterval = 0;
|
||||
}
|
||||
|
||||
Integer preCloseInterestCalculationStrategy = command
|
||||
.integerValueOfParameterNamed(LoanProductConstants.preCloseInterestCalculationStrategyParamName);
|
||||
if (preCloseInterestCalculationStrategy == null) {
|
||||
preCloseInterestCalculationStrategy = LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue();
|
||||
}
|
||||
|
||||
return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod,
|
||||
recurrenceFrequency, recurrenceInterval, recurrenceOnDate, isArrearsBasedOnOriginalSchedule);
|
||||
recurrenceFrequency, recurrenceInterval, recurrenceOnDate, isArrearsBasedOnOriginalSchedule,
|
||||
preCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
private LoanProductInterestRecalculationDetails(final Integer interestRecalculationCompoundingMethod,
|
||||
final Integer rescheduleStrategyMethod, final Integer restFrequencyType, final Integer restInterval,
|
||||
final Date restFrequencyDate, final boolean isArrearsBasedOnOriginalSchedule) {
|
||||
final Date restFrequencyDate, final boolean isArrearsBasedOnOriginalSchedule, final Integer preCloseInterestCalculationStrategy) {
|
||||
this.interestRecalculationCompoundingMethod = interestRecalculationCompoundingMethod;
|
||||
this.rescheduleStrategyMethod = rescheduleStrategyMethod;
|
||||
this.restFrequencyType = restFrequencyType;
|
||||
this.restInterval = restInterval;
|
||||
this.restFrequencyDate = restFrequencyDate;
|
||||
this.isArrearsBasedOnOriginalSchedule = isArrearsBasedOnOriginalSchedule;
|
||||
this.preCloseInterestCalculationStrategy = preCloseInterestCalculationStrategy;
|
||||
}
|
||||
|
||||
public void updateProduct(final LoanProduct loanProduct) {
|
||||
@@ -183,6 +195,16 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
|
||||
this.isArrearsBasedOnOriginalSchedule = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInIntegerParameterNamed(LoanProductConstants.preCloseInterestCalculationStrategyParamName,
|
||||
this.preCloseInterestCalculationStrategy)) {
|
||||
Integer newValue = command.integerValueOfParameterNamed(LoanProductConstants.preCloseInterestCalculationStrategyParamName);
|
||||
if (newValue == null) {
|
||||
newValue = LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue();
|
||||
}
|
||||
actualChanges.put(LoanProductConstants.preCloseInterestCalculationStrategyParamName, newValue);
|
||||
this.preCloseInterestCalculationStrategy = newValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public LocalDate getRestFrequencyLocalDate() {
|
||||
@@ -201,8 +223,11 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable
|
||||
return this.restInterval;
|
||||
}
|
||||
|
||||
|
||||
public boolean isArrearsBasedOnOriginalSchedule() {
|
||||
return this.isArrearsBasedOnOriginalSchedule;
|
||||
}
|
||||
|
||||
public LoanPreCloseInterestCalculationStrategy preCloseInterestCalculationStrategy() {
|
||||
return LoanPreCloseInterestCalculationStrategy.fromInt(this.preCloseInterestCalculationStrategy);
|
||||
}
|
||||
}
|
||||
|
||||
+51
-34
@@ -26,6 +26,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.RecalculationFrequencyType;
|
||||
@@ -43,40 +44,47 @@ public final class LoanProductDataValidator {
|
||||
/**
|
||||
* The parameters supported for this command.
|
||||
*/
|
||||
private final Set<String> supportedParameters = new HashSet<>(Arrays.asList("locale", "dateFormat", "name", "description", "fundId",
|
||||
"currencyCode", "digitsAfterDecimal", "inMultiplesOf", "principal", "minPrincipal", "maxPrincipal", "repaymentEvery",
|
||||
"numberOfRepayments", "minNumberOfRepayments", "maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod",
|
||||
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
|
||||
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment",
|
||||
"graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate",
|
||||
"closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(), LoanProductConstants.useBorrowerCycleParameterName,
|
||||
LoanProductConstants.principalVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, LoanProductConstants.shortName,
|
||||
LoanProductConstants.multiDisburseLoanParameterName, LoanProductConstants.outstandingLoanBalanceParameterName,
|
||||
LoanProductConstants.maxTrancheCountParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName,
|
||||
LoanProductConstants.overdueDaysForNPAParameterName, LoanProductConstants.isInterestRecalculationEnabledParameterName,
|
||||
LoanProductConstants.daysInYearTypeParameterName, LoanProductConstants.daysInMonthTypeParameterName,
|
||||
LoanProductConstants.rescheduleStrategyMethodParameterName,
|
||||
LoanProductConstants.interestRecalculationCompoundingMethodParameterName,
|
||||
LoanProductConstants.recalculationRestFrequencyDateParamName,
|
||||
LoanProductConstants.recalculationRestFrequencyIntervalParameterName,
|
||||
LoanProductConstants.recalculationRestFrequencyTypeParameterName,
|
||||
LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName,
|
||||
LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName,
|
||||
LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName,
|
||||
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName, LoanProductConstants.principalThresholdForLastInstallmentParamName,
|
||||
LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName, LoanProductConstants.canDefineEmiAmountParamName,
|
||||
LoanProductConstants.installmentAmountInMultiplesOfParamName));
|
||||
private final Set<String> supportedParameters = new HashSet<>(
|
||||
Arrays.asList("locale", "dateFormat", "name", "description", "fundId", "currencyCode", "digitsAfterDecimal", "inMultiplesOf",
|
||||
"principal", "minPrincipal", "maxPrincipal", "repaymentEvery", "numberOfRepayments", "minNumberOfRepayments",
|
||||
"maxNumberOfRepayments", "repaymentFrequencyType", "interestRatePerPeriod", "minInterestRatePerPeriod",
|
||||
"maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
|
||||
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment",
|
||||
"graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate",
|
||||
"closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.OVERPAYMENT.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.TRANSFERS_SUSPENSE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.LOSSES_WRITTEN_OFF.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTIES_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PAYMENT_CHANNEL_FUND_SOURCE_MAPPING.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FEE_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_RECOVERY.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.PENALTY_INCOME_ACCOUNT_MAPPING.getValue(),
|
||||
LoanProductConstants.useBorrowerCycleParameterName,
|
||||
LoanProductConstants.principalVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.interestRateVariationsForBorrowerCycleParameterName,
|
||||
LoanProductConstants.numberOfRepaymentVariationsForBorrowerCycleParameterName, LoanProductConstants.shortName,
|
||||
LoanProductConstants.multiDisburseLoanParameterName, LoanProductConstants.outstandingLoanBalanceParameterName,
|
||||
LoanProductConstants.maxTrancheCountParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName,
|
||||
LoanProductConstants.overdueDaysForNPAParameterName, LoanProductConstants.isInterestRecalculationEnabledParameterName,
|
||||
LoanProductConstants.daysInYearTypeParameterName, LoanProductConstants.daysInMonthTypeParameterName,
|
||||
LoanProductConstants.rescheduleStrategyMethodParameterName,
|
||||
LoanProductConstants.interestRecalculationCompoundingMethodParameterName,
|
||||
LoanProductConstants.recalculationRestFrequencyDateParamName,
|
||||
LoanProductConstants.recalculationRestFrequencyIntervalParameterName,
|
||||
LoanProductConstants.recalculationRestFrequencyTypeParameterName,
|
||||
LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName,
|
||||
LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, LoanProductConstants.mandatoryGuaranteeParamName,
|
||||
LoanProductConstants.holdGuaranteeFundsParamName, LoanProductConstants.minimumGuaranteeFromGuarantorParamName,
|
||||
LoanProductConstants.minimumGuaranteeFromOwnFundsParamName,
|
||||
LoanProductConstants.principalThresholdForLastInstallmentParamName,
|
||||
LoanProductConstants.accountMovesOutOfNPAOnlyOnArrearsCompletionParamName,
|
||||
LoanProductConstants.canDefineEmiAmountParamName, LoanProductConstants.installmentAmountInMultiplesOfParamName,
|
||||
LoanProductConstants.preCloseInterestCalculationStrategyParamName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
@@ -537,6 +545,15 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter(LoanProductConstants.isArrearsBasedOnOriginalScheduleParamName)
|
||||
.value(isArrearsBasedOnOriginalSchedule).notNull().isOneOfTheseValues(true, false);
|
||||
}
|
||||
|
||||
final Integer preCloseInterestCalculationStrategy = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(
|
||||
LoanProductConstants.preCloseInterestCalculationStrategyParamName, element);
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanProductConstants.preCloseInterestCalculationStrategyParamName)
|
||||
.value(preCloseInterestCalculationStrategy)
|
||||
.ignoreIfNull()
|
||||
.inMinMaxRange(LoanPreCloseInterestCalculationStrategy.getMinValue(), LoanPreCloseInterestCalculationStrategy.getMaxValue());
|
||||
}
|
||||
|
||||
public void validateForUpdate(final String json, final LoanProduct loanProduct) {
|
||||
|
||||
+2
@@ -38,5 +38,7 @@ public interface LoanDropdownReadPlatformService {
|
||||
List<EnumOptionData> retrieveRescheduleStrategyTypeOptions();
|
||||
|
||||
List<EnumOptionData> retrieveInterestRecalculationFrequencyTypeOptions();
|
||||
|
||||
List<EnumOptionData> retrivePreCloseInterestCalculationStrategyOptions();
|
||||
|
||||
}
|
||||
+25
-14
@@ -17,7 +17,7 @@ import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.r
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyNthDayType;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.repaymentFrequencyType;
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.rescheduleStrategyType;
|
||||
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.preCloseInterestCalculationStrategy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -33,6 +33,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
@@ -93,21 +94,22 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveRepaymentFrequencyOptionsForNthDayOfMonth() {
|
||||
final List<EnumOptionData> repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyNthDayType(NthDayType.ONE),
|
||||
repaymentFrequencyNthDayType(NthDayType.TWO), repaymentFrequencyNthDayType(NthDayType.THREE),
|
||||
repaymentFrequencyNthDayType(NthDayType.FOUR));
|
||||
return repaymentFrequencyOptions;
|
||||
public List<EnumOptionData> retrieveRepaymentFrequencyOptionsForNthDayOfMonth() {
|
||||
final List<EnumOptionData> repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyNthDayType(NthDayType.ONE),
|
||||
repaymentFrequencyNthDayType(NthDayType.TWO), repaymentFrequencyNthDayType(NthDayType.THREE),
|
||||
repaymentFrequencyNthDayType(NthDayType.FOUR));
|
||||
return repaymentFrequencyOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveRepaymentFrequencyOptionsForDaysOfWeek() {
|
||||
|
||||
final List<EnumOptionData> repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyDayOfWeekType(DayOfWeekType.SUNDAY),
|
||||
repaymentFrequencyDayOfWeekType(DayOfWeekType.MONDAY),repaymentFrequencyDayOfWeekType(DayOfWeekType.TUESDAY),repaymentFrequencyDayOfWeekType(DayOfWeekType.WEDNESDAY),
|
||||
repaymentFrequencyDayOfWeekType(DayOfWeekType.THURSDAY),repaymentFrequencyDayOfWeekType(DayOfWeekType.FRIDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.SATURDAY));
|
||||
return repaymentFrequencyOptions;
|
||||
}
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveRepaymentFrequencyOptionsForDaysOfWeek() {
|
||||
|
||||
final List<EnumOptionData> repaymentFrequencyOptions = Arrays.asList(repaymentFrequencyDayOfWeekType(DayOfWeekType.SUNDAY),
|
||||
repaymentFrequencyDayOfWeekType(DayOfWeekType.MONDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.TUESDAY),
|
||||
repaymentFrequencyDayOfWeekType(DayOfWeekType.WEDNESDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.THURSDAY),
|
||||
repaymentFrequencyDayOfWeekType(DayOfWeekType.FRIDAY), repaymentFrequencyDayOfWeekType(DayOfWeekType.SATURDAY));
|
||||
return repaymentFrequencyOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrieveInterestRateFrequencyTypeOptions() {
|
||||
@@ -170,4 +172,13 @@ public class LoanDropdownReadPlatformServiceImpl implements LoanDropdownReadPlat
|
||||
interestRecalculationFrequencyType(RecalculationFrequencyType.MONTHLY));
|
||||
return interestRecalculationFrequencyTypeOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnumOptionData> retrivePreCloseInterestCalculationStrategyOptions() {
|
||||
|
||||
final List<EnumOptionData> preCloseInterestCalculationStrategyOptions = Arrays.asList(
|
||||
preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE),
|
||||
preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE));
|
||||
return preCloseInterestCalculationStrategyOptions;
|
||||
}
|
||||
}
|
||||
+21
@@ -20,6 +20,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanPreCloseInterestCalculationStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductParamType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductValueConditionType;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanRescheduleStrategyMethod;
|
||||
@@ -595,4 +596,24 @@ public class LoanEnumerations {
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData preCloseInterestCalculationStrategy(final int id) {
|
||||
return preCloseInterestCalculationStrategy(LoanPreCloseInterestCalculationStrategy.fromInt(id));
|
||||
}
|
||||
|
||||
public static EnumOptionData preCloseInterestCalculationStrategy(final LoanPreCloseInterestCalculationStrategy type) {
|
||||
EnumOptionData optionData = null;
|
||||
switch (type) {
|
||||
case TILL_PRE_CLOSURE_DATE:
|
||||
optionData = new EnumOptionData(LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getValue().longValue(),
|
||||
LoanPreCloseInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE.getCode(), "Till preclose Date");
|
||||
break;
|
||||
case TILL_REST_FREQUENCY_DATE:
|
||||
optionData = new EnumOptionData(LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE.getValue().longValue(),
|
||||
LoanPreCloseInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE.getCode(), "Till rest Frequency Date");
|
||||
break;
|
||||
}
|
||||
return optionData;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -161,6 +161,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "lp.allow_multiple_disbursals as multiDisburseLoan, lp.max_disbursals as maxTrancheCount, lp.max_outstanding_loan_balance as outstandingLoanBalance, "
|
||||
+ "lp.days_in_month_enum as daysInMonth, lp.days_in_year_enum as daysInYear, lp.interest_recalculation_enabled as isInterestRecalculationEnabled, "
|
||||
+ "lp.can_define_fixed_emi_amount as canDefineInstallmentAmount, lp.instalment_amount_in_multiples_of as installmentAmountInMultiplesOf, "
|
||||
+ "lpr.pre_close_interest_calculation_strategy as preCloseInterestCalculationStrategy, "
|
||||
+ "lpr.id as lprId, lpr.product_id as productId, lpr.compound_type_enum as compoundType, lpr.reschedule_strategy_enum as rescheduleStrategy, "
|
||||
+ "lpr.rest_frequency_type_enum as restFrequencyEnum, lpr.rest_frequency_interval as restFrequencyInterval, "
|
||||
+ "lpr.rest_freqency_date as restFrequencyDate, lpr.arrears_based_on_original_schedule as isArrearsBasedOnOriginalSchedule, "
|
||||
@@ -281,6 +282,7 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final boolean canDefineInstallmentAmount = rs.getBoolean("canDefineInstallmentAmount");
|
||||
final boolean isInterestRecalculationEnabled = rs.getBoolean("isInterestRecalculationEnabled");
|
||||
|
||||
|
||||
LoanProductInterestRecalculationData interestRecalculationData = null;
|
||||
if (isInterestRecalculationEnabled) {
|
||||
|
||||
@@ -296,9 +298,13 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final int restFrequencyInterval = JdbcSupport.getInteger(rs, "restFrequencyInterval");
|
||||
final LocalDate restFrequencyDate = JdbcSupport.getLocalDate(rs, "restFrequencyDate");
|
||||
final boolean isArrearsBasedOnOriginalSchedule = rs.getBoolean("isArrearsBasedOnOriginalSchedule");
|
||||
final int preCloseInterestCalculationStrategyEnumValue = JdbcSupport.getInteger(rs, "preCloseInterestCalculationStrategy");
|
||||
final EnumOptionData preCloseInterestCalculationStrategy = LoanEnumerations
|
||||
.preCloseInterestCalculationStrategy(preCloseInterestCalculationStrategyEnumValue);
|
||||
|
||||
interestRecalculationData = new LoanProductInterestRecalculationData(lprId, productId,
|
||||
interestRecalculationCompoundingType, rescheduleStrategyType, restFrequencyType, restFrequencyInterval,
|
||||
restFrequencyDate, isArrearsBasedOnOriginalSchedule);
|
||||
restFrequencyDate, isArrearsBasedOnOriginalSchedule, preCloseInterestCalculationStrategy);
|
||||
}
|
||||
|
||||
final boolean holdGuaranteeFunds = rs.getBoolean("holdGuaranteeFunds");
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `m_product_loan_recalculation_details`
|
||||
ADD COLUMN `pre_close_interest_calculation_strategy` SMALLINT(3) NOT NULL DEFAULT '1';
|
||||
Reference in New Issue
Block a user