From 91ea2f0e2e00433f9b951c562ef31f1d90e37ba7 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Wed, 17 Sep 2014 12:20:41 +0530 Subject: [PATCH] MIFOSX-1524 : corrected Interest waiver --- .../portfolio/loanaccount/domain/Loan.java | 4 +- .../LoanRepaymentScheduleInstallment.java | 2 +- .../loanaccount/domain/LoanSummary.java | 5 + ...RepaymentScheduleTransactionProcessor.java | 8 +- .../domain/AbstractLoanScheduleGenerator.java | 161 +++++++++++------- .../domain/LoanScheduleGenerator.java | 2 +- .../service/LoanScheduleAssembler.java | 5 +- ...cheduleCalculationPlatformServiceImpl.java | 10 +- 8 files changed, 123 insertions(+), 74 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index b3a967a4b..b9f8153fc 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -1991,7 +1991,7 @@ public class Loan extends AbstractPersistable { return isDisbursementMissed; } - private BigDecimal getDisbursedAmount() { + public BigDecimal getDisbursedAmount() { BigDecimal principal = BigDecimal.ZERO; for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) { if (disbursementDetail.actualDisbursementDate() != null) { @@ -4282,7 +4282,7 @@ public class Loan extends AbstractPersistable { this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate()); installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(), - LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc); + LocalDate.now(), getInterestChargedFromDate(), loanApplicationTerms, mc, charges()); } else { installment = this.getTotalOutstandingOnLoan(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java index 1861ed2fc..38bc2bb76 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanRepaymentScheduleInstallment.java @@ -436,7 +436,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus final MonetaryCurrency currency = transactionAmountRemaining.getCurrency(); Money waivedFeeChargesPortionOfTransaction = Money.zero(currency); - final Money feesDue = getPenaltyChargesOutstanding(currency); + final Money feesDue = getFeeChargesOutstanding(currency); if (transactionAmountRemaining.isGreaterThanOrEqualTo(feesDue)) { this.feeChargesWaived = getFeeChargesWaived(currency).plus(feesDue).getAmount(); waivedFeeChargesPortionOfTransaction = waivedFeeChargesPortionOfTransaction.plus(feesDue); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java index 2e04f05f5..463121248 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanSummary.java @@ -301,4 +301,9 @@ public final class LoanSummary { public BigDecimal getTotalPenaltyChargesCharged() { return this.totalPenaltyChargesCharged; } + + + public BigDecimal getTotalPrincipalWrittenOff() { + return this.totalPrincipalWrittenOff; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 415522881..2abf84fbc 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -123,7 +123,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen transactionstoBeProcessed.add(loanTransaction); } } - + for (final LoanTransaction loanTransaction : transactionstoBeProcessed) { if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) { @@ -451,11 +451,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen transactionDate = recalculationDates.get(transactionDate); } final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction); - newLoanTransaction.resetDerivedComponents(); + if (newLoanTransaction.isRepayment() || newLoanTransaction.isInterestWaiver() || newLoanTransaction.isRecoveryRepayment()) { + newLoanTransaction.resetDerivedComponents(); + } Money unProcessed = processTransaction(newLoanTransaction, currency, installments, amountToProcess); if (transactionDate.isAfter(installment.getFromDate()) && transactionDate.isBefore(installment.getDueDate())) { Money earlyPayment = currentInstallmentOutstanding.minus(installment.getTotalOutstanding(currency)); - if (earlyPayment.isGreaterThanZero()) { + if (earlyPayment.isGreaterThanZero() && (newLoanTransaction.isRepayment() || newLoanTransaction.isInterestWaiver() || newLoanTransaction.isRecoveryRepayment())) { earlypaymentDetail.add(new RecalculationDetail(false, transactionDate, null, earlyPayment, false)); } currentInstallmentOutstanding = installment.getTotalOutstanding(currency); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index 4bdca5d96..f4c5bdda3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -53,12 +53,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final LoanApplicationTerms loanApplicationTerms, final Set loanCharges, final boolean isHolidayEnabled, final List holidays, final WorkingDays workingDays) { final List diffAmt = null; - return generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, diffAmt); + final LocalDate prepayDate = null; + return generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, diffAmt, + prepayDate); } private LoanScheduleModel generate(final MathContext mc, final ApplicationCurrency applicationCurrency, final LoanApplicationTerms loanApplicationTerms, final Set loanCharges, final boolean isHolidayEnabled, - final List holidays, final WorkingDays workingDays, List diffAmt) { + final List holidays, final WorkingDays workingDays, List diffAmt, LocalDate prepayDate) { // 1. generate list of proposed schedule due dates final LocalDate loanEndDate = this.scheduledDateGenerator.getLastRepaymentDate(loanApplicationTerms, isHolidayEnabled, holidays, @@ -191,8 +193,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener if (loanApplicationTerms.isMultiDisburseLoan()) { loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate); final Collection disbursementDatas = new ArrayList<>(); - disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, scheduledDueDate, disbursementDatas, - diffAmt != null); + LocalDate tillDate = scheduledDueDate; + if (prepayDate != null && prepayDate.isBefore(tillDate)) { + tillDate = prepayDate; + } + disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, tillDate, disbursementDatas, diffAmt != null); principalDisbursed = principalDisbursed.plus(disburseAmt); loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt)); outstandingBalance = outstandingBalance.plus(disburseAmt); @@ -207,11 +212,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement( disbursementData.disbursementDate(), disbursedAmt, chargesDueAtTimeOfDisbursement); periods.add(disbursementPeriod); - if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)) { + + if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest) + && (prepayDate == null || prepayDate.isAfter(disbursementData.disbursementDate()) || disbursementData + .isDisbursed())) { interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableInFullInstallment, interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, disbursementData.disbursementDate(), - scheduledDueDate)); + tillDate)); } } } @@ -224,14 +232,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener case REDUCE_EMI_AMOUNT: loanApplicationTerms.setFixedEmiAmount(null); outstandingBalance = outstandingBalance.minus(reducePrincipal); - balanceForcalculation = outstandingBalance; + balanceForcalculation = balanceForcalculation.minus(reducePrincipal); totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal); reducePrincipal = reducePrincipal.zero(); break; case REDUCE_NUMBER_OF_INSTALLMENTS: loanApplicationTerms.setFixedEmiAmount(fixedEmiAmount.getAmount()); outstandingBalance = outstandingBalance.minus(reducePrincipal); - balanceForcalculation = outstandingBalance; + balanceForcalculation = balanceForcalculation.minus(reducePrincipal); totalCumulativePrincipal = totalCumulativePrincipal.plus(reducePrincipal); reducePrincipal = reducePrincipal.zero(); break; @@ -1022,11 +1030,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener List installments = previousSchedule; List removeInstallments = new ArrayList<>(); MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency(); - LoanTransaction preCloseTransaction = getPreclosureTransaction(transactions, installments, currency, loanApplicationTerms, mc); + LoanTransaction preCloseTransaction = getPreclosureTransaction(transactions, installments, currency, loanApplicationTerms, mc, + loanCharges); + Integer updatedInstallmentNumber = 1; for (LoanRepaymentScheduleInstallment installment : installments) { if (installment.isRecalculatedInterestComponent() && recalculateFrom != null && !recalculateFrom.isAfter(installment.getDueDate())) { removeInstallments.add(installment); + } else { + installment.updateInstallmentNumber(updatedInstallmentNumber++); } } installments.removeAll(removeInstallments); @@ -1097,12 +1109,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final List recalculationDetails, final Map retainRecalculationDetails, boolean recalculate, int installmentNumber, List processTransactions, LoanTransaction preCloseTransaction) { LoanTransaction loanTransaction = processTransactions.remove(processTransactions.size() - 1); - if (loanTransaction == preCloseTransaction) { + if (!(loanTransaction.isRepayment() || loanTransaction.isInterestWaiver())) { processTransactions.add(loanTransaction); return loanScheduleModel; } - LocalDate processTransactionsForInterestCompound = getNextRestScheduleDate(loanTransaction.getTransactionDate().minusDays(1), - loanApplicationTerms, isHolidayEnabled, holidays, workingDays); + LocalDate processTransactionsForInterestCompound = loanTransaction.getTransactionDate(); + if (loanTransaction != preCloseTransaction) { + processTransactionsForInterestCompound = getNextRestScheduleDate(loanTransaction.getTransactionDate().minusDays(1), + loanApplicationTerms, isHolidayEnabled, holidays, workingDays); + } List scheduleInstallments = getInstallmentsForInterestCompound(installments, processTransactionsForInterestCompound, retainRecalculationDetails.keySet()); @@ -1214,7 +1229,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener final List holidays, final WorkingDays workingDays, final List transactions, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber, final List installments, final List recalculationDetails, - final int skipRecalculation, LoanTransaction preCloseTransaction) { + final int skipRecalculation, final LoanTransaction preCloseTransaction) { boolean processRecalculate = false; int processedInstallmentNumber = installmentNumber; @@ -1324,8 +1339,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener LoanScheduleModel model = null; recalculationDetails.addAll(diffAmt); if (processRecalculate) { + LocalDate prepayDate = null; + if (preCloseTransaction != null) { + prepayDate = preCloseTransaction.getTransactionDate(); + } model = generate(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled, holidays, workingDays, - recalculationDetails); + recalculationDetails, prepayDate); } return new RecalculatedSchedule(model, processedInstallmentNumber); } @@ -1392,7 +1411,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener @Override public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final List installments, MonetaryCurrency currency, final LocalDate onDate, LocalDate interestChargedFromLocalDate, - LoanApplicationTerms loanApplicationTerms, MathContext mc) { + LoanApplicationTerms loanApplicationTerms, MathContext mc, Set charges) { Money feeCharges = Money.zero(currency); Money penaltyCharges = Money.zero(currency); Money totalPrincipal = Money.zero(currency); @@ -1400,12 +1419,21 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener LocalDate calculateInterestFrom = onDate; LocalDate dueDate = onDate; LocalDate repaymentDueDate = null; + LocalDate periodStartDate = onDate; boolean isInterestRepayment = false; Money inerestForCurrentInstallment = Money.zero(currency); Money principalOutstanding = Money.zero(currency); Integer periodNumber = 0; LocalDate lastDueDate = loanApplicationTerms.getExpectedDisbursementDate(); for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { + if (!currentInstallment.getDueDate().isAfter(LocalDate.now())) { + calculateInterestFrom = currentInstallment.getFromDate(); + if (currentInstallment.isRecalculatedInterestComponent()) { + isInterestRepayment = true; + } else { + isInterestRepayment = false; + } + } if (currentInstallment.isNotFullyPaidOff()) { if (!currentInstallment.getDueDate().isAfter(onDate)) { totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency)); @@ -1417,8 +1445,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener totalPrincipal = totalPrincipal.minus(currentInstallment.getPrincipalCompleted(currency)); totalInterest = totalInterest.minus(currentInstallment.getInterestPaid(currency)).minus( currentInstallment.getInterestWaived(currency)); - if (currentInstallment.getFromDate().isBefore(calculateInterestFrom)) { - calculateInterestFrom = currentInstallment.getFromDate(); + if (currentInstallment.getFromDate().isBefore(periodStartDate)) { + if (!isInterestRepayment) { + calculateInterestFrom = currentInstallment.getFromDate(); + } + periodStartDate = currentInstallment.getFromDate(); dueDate = currentInstallment.getDueDate(); inerestForCurrentInstallment = currentInstallment.getInterestCharged(currency); if (currentInstallment.isRecalculatedInterestComponent()) { @@ -1443,8 +1474,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener calculateInterestFrom = interestChargedFromLocalDate; } Money interest = Money.zero(currency); - - if (calculateInterestFrom.isBefore(onDate)) { + LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate( + loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, true); + final LocalDate idealDisbursementDate = this.scheduledDateGenerator.idealDisbursementDateBasedOnFirstRepaymentDate( + loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate); + if (calculateInterestFrom.isBefore(onDate) && !periodStartDate.isBefore(idealDisbursementDate)) { boolean useDailyInterest = false; if (repaymentDueDate == null) { if (isInterestRepayment) { @@ -1453,7 +1487,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener repaymentDueDate = dueDate; } int daysInPeriodApplicableForInterest = Days.daysBetween(calculateInterestFrom, repaymentDueDate).getDays(); - int days = Days.daysBetween(calculateInterestFrom, onDate).getDays(); + int days = Days.daysBetween(periodStartDate, onDate).getDays(); + int actualPeriodDays = Days.daysBetween(periodStartDate, repaymentDueDate).getDays(); BigDecimal interestForInstallment = BigDecimal.ZERO; if (useDailyInterest) { interestForInstallment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc, @@ -1462,11 +1497,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days)); } else { - LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate( - loanApplicationTerms.getExpectedDisbursementDate(), loanApplicationTerms, true); - final LocalDate idealDisbursementDate = this.scheduledDateGenerator - .idealDisbursementDateBasedOnFirstRepaymentDate(loanApplicationTerms.getLoanTermPeriodFrequencyType(), - loanApplicationTerms.getRepaymentEvery(), firstRepaymentdate); + LocalDate periodStartDateApplicableForInterest = calculateInterestFrom; if (calculateInterestFrom.isBefore(idealDisbursementDate)) { if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) { @@ -1481,58 +1512,48 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener .calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, repaymentDueDate, loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery()); - + Money calculateInterestOnPrincipal = principalOutstanding; + final Collection disbursementDatas = new ArrayList<>(); if (loanApplicationTerms.isMultiDisburseLoan()) { BigDecimal reducePrincipal = disbursementAfterPeriod(loanApplicationTerms, repaymentDueDate, lastDueDate); principalOutstanding = principalOutstanding.minus(reducePrincipal); + calculateInterestOnPrincipal = principalOutstanding; + BigDecimal currentPeriodDisbursal = disbursementForPeriod(loanApplicationTerms, periodStartDate, repaymentDueDate, + disbursementDatas, true); + calculateInterestOnPrincipal = calculateInterestOnPrincipal.minus(currentPeriodDisbursal); } PrincipalInterest principalInterest = loanApplicationTerms.calculateTotalInterestForPeriod( this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, - totalOutstandingInterestPaymentDueToGrace.zero(), daysInPeriodApplicableForInterest, principalOutstanding); - interestForInstallment = principalInterest.interest().getAmount(); - Money alteredPrincipal = principalOutstanding; - Money reducePricipal = principalOutstanding.zero(); + totalOutstandingInterestPaymentDueToGrace.zero(), daysInPeriodApplicableForInterest, calculateInterestOnPrincipal); + interestForInstallment = BigDecimal.valueOf(calculateInterestForDays(daysInPeriodApplicableForInterest, principalInterest + .interest().getAmount(), actualPeriodDays)); + BigDecimal interestToBeAdded = BigDecimal.ZERO; BigDecimal interestToBeReduced = BigDecimal.ZERO; - BigDecimal interestForExtraPrincipal = BigDecimal.ZERO; if (loanApplicationTerms.isMultiDisburseLoan()) { - final Collection disbursementDatas = new ArrayList<>(); - BigDecimal addedPrincipal = disbursementForPeriod(loanApplicationTerms, calculateInterestFrom, repaymentDueDate, - disbursementDatas, true); - alteredPrincipal = alteredPrincipal.minus(addedPrincipal); for (DisbursementData disbursementData : disbursementDatas) { Money disbursedAmt = Money.of(currency, disbursementData.amount()); - if (disbursementData.disbursementDate().isAfter(calculateInterestFrom)) { - LocalDate calculateTill = disbursementData.disbursementDate(); + LocalDate calculateTill = disbursementData.disbursementDate(); + if (!onDate.isAfter(calculateTill)) { + principalOutstanding = principalOutstanding.minus(disbursedAmt); + } else { + interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, + periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest, + interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateTill, onDate)); + } + + if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)) { interestToBeReduced = interestToBeReduced.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest, - interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateInterestFrom, calculateTill)); - - if (onDate.isAfter(calculateTill)) { - interestForExtraPrincipal = interestForExtraPrincipal.add(calculateInterestForSpecificDays(mc, - loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace, - daysInPeriodApplicableForInterest, interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, - calculateTill, onDate)); - } else { - reducePricipal = reducePricipal.plus(disbursedAmt); - } + interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateTill, repaymentDueDate)); } - } - interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment).plus(interestToBeReduced)); - BigDecimal interestForCloseDate = BigDecimal.ZERO; - if (alteredPrincipal.isNotEqualTo(principalOutstanding)) { - interestForCloseDate = interestForExtraPrincipal; - interestForCloseDate = interestForCloseDate.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, - periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest, - interestCalculationGraceOnRepaymentPeriodFraction, alteredPrincipal, calculateInterestFrom, onDate)); - } else { - interestForCloseDate = interestForCloseDate.add(BigDecimal.valueOf(calculateInterestForDays( - daysInPeriodApplicableForInterest, interestForInstallment, days))); - } - interest = interest.plus(interestForCloseDate); - principalOutstanding = principalOutstanding.minus(reducePricipal); - } + } + } + interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment).minus(interestToBeReduced)); + BigDecimal interestForCloseDate = BigDecimal.valueOf(calculateInterestForDays(actualPeriodDays, interestForInstallment, + days)); + interest = interest.plus(interestForCloseDate).plus(interestToBeAdded); } } @@ -1540,6 +1561,17 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener totalInterest = totalInterest.plus(interest); totalPrincipal = totalPrincipal.plus(principalOutstanding); + for (LoanCharge loanCharge : charges) { + if (loanCharge.isActive() && loanCharge.isNotFullyPaid() && !loanCharge.isWaived() + && loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStartDate, onDate)) { + if (loanCharge.isFeeCharge()) { + feeCharges = feeCharges.plus(loanCharge.amountOutstanding()); + } else { + penaltyCharges = penaltyCharges.plus(loanCharge.amountOutstanding()); + } + } + } + return new LoanRepaymentScheduleInstallment(null, 0, onDate, onDate, totalPrincipal.getAmount(), totalInterest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount(), false); } @@ -1588,7 +1620,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener private LoanTransaction getPreclosureTransaction(List loanTransactions, List installments, MonetaryCurrency currency, final LoanApplicationTerms applicationTerms, - MathContext mc) { + MathContext mc, final Set loanCharges) { LoanTransaction precloseTransaction = null; for (LoanTransaction loanTransaction : loanTransactions) { if (precloseTransaction == null || precloseTransaction.getTransactionDate().isBefore(loanTransaction.getTransactionDate())) { @@ -1597,7 +1629,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener } if (precloseTransaction != null) { LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = calculatePrepaymentAmount(installments, currency, - precloseTransaction.getTransactionDate(), applicationTerms.getInterestChargedFromLocalDate(), applicationTerms, mc); + precloseTransaction.getTransactionDate(), applicationTerms.getInterestChargedFromLocalDate(), applicationTerms, mc, + loanCharges); if (precloseTransaction.getAmount(currency).isLessThan(loanRepaymentScheduleInstallment.getTotalOutstanding(currency))) { precloseTransaction = null; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java index 4450461ee..214c04444 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java @@ -33,7 +33,7 @@ public interface LoanScheduleGenerator { List previousSchedule, LocalDate recalculateFrom); LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List installments, MonetaryCurrency currency, - LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms, MathContext mc); + LocalDate onDate, LocalDate interestChargedFromLocalDate, LoanApplicationTerms loanApplicationTerms, MathContext mc, Set charges); LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final boolean isHolidayEnabled, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index c58068442..73337ecdb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -385,7 +385,8 @@ public class LoanScheduleAssembler { } public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List installments, - MonetaryCurrency currency, LocalDate onDate, LoanApplicationTerms loanApplicationTerms,final Long officeId) { + MonetaryCurrency currency, LocalDate onDate, LoanApplicationTerms loanApplicationTerms, final Long officeId, + final Set loanCharges) { final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod()); final RoundingMode roundingMode = RoundingMode.HALF_EVEN; final MathContext mc = new MathContext(8, roundingMode); @@ -404,7 +405,7 @@ public class LoanScheduleAssembler { } return loanScheduleGenerator.calculatePrepaymentAmount(installments, currency, nextScheduleDate, - loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms, mc); + loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms, mc, loanCharges); } private void validateDisbursementDateIsOnNonWorkingDay(final LocalDate disbursementDate, final WorkingDays workingDays) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java index 3b85035e0..e7b803a91 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.loanaccount.loanschedule.service; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -127,6 +128,12 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC if (!loan.repaymentScheduleDetail().isInterestRecalculationEnabled() || loan.isNpa() || !loan.status().isActive() || !loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) { return; } + if (loan.loanProduct().isMultiDisburseLoan()) { + BigDecimal disbursedAmount = loan.getDisbursedAmount(); + BigDecimal principalRepaid = loan.getLoanSummary().getTotalPrincipalRepaid(); + BigDecimal principalWrittenOff = loan.getLoanSummary().getTotalPrincipalWrittenOff(); + if (disbursedAmount.subtract(principalWrittenOff).subtract(principalRepaid).compareTo(BigDecimal.ZERO) != 1) { return; } + } MonetaryCurrency currency = loan.getCurrency(); Money totalPrincipal = Money.zero(currency); final List futureInstallments = new ArrayList<>(); @@ -147,7 +154,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency, calculatedRepaymentsStartingFromDate, restCalendarInstance); LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount( - loan.fetchRepaymentScheduleInstallments(), currency, LocalDate.now(), loanApplicationTerms, loan.getOfficeId()); + loan.fetchRepaymentScheduleInstallments(), currency, LocalDate.now(), loanApplicationTerms, loan.getOfficeId(), + loan.charges()); Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus( loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency)); Money interestDue = Money.zero(currency);