From 91ea2f0e2e00433f9b951c562ef31f1d90e37ba7 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Wed, 17 Sep 2014 12:20:41 +0530 Subject: [PATCH 1/4] 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); From 7da550fe7cb5c594d37af396a112ce2ed9eb2967 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Wed, 17 Sep 2014 13:23:13 +0530 Subject: [PATCH 2/4] MIFOSX-1535 : added condition to set the default value --- .../domain/LoanProductInterestRecalculationDetails.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java index 97de3bd5c..dd8817c46 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanproduct/domain/LoanProductInterestRecalculationDetails.java @@ -78,12 +78,13 @@ public class LoanProductInterestRecalculationDetails extends AbstractPersistable RecalculationFrequencyType frequencyType = RecalculationFrequencyType.fromInt(recurrenceFrequency); Date recurrenceOnDate = null; if (recurrenceOnLocalDate != null) { - if (frequencyType.isSameAsRepayment()) { - recurrenceInterval = 0; - } else { + if (!frequencyType.isSameAsRepayment()) { recurrenceOnDate = recurrenceOnLocalDate.toDate(); } } + if (frequencyType.isSameAsRepayment()) { + recurrenceInterval = 0; + } return new LoanProductInterestRecalculationDetails(interestRecalculationCompoundingMethod, loanRescheduleStrategyMethod, recurrenceFrequency, recurrenceInterval, recurrenceOnDate); From 5b0dc5b77ba18f536c97cd9298ef91bc497e19b8 Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Wed, 17 Sep 2014 20:19:59 +0530 Subject: [PATCH 3/4] MIFOSX-1541 : corrected prepayment and interest calculation on date change --- .../portfolio/loanaccount/domain/Loan.java | 31 ++++++++------ .../LoanAccrualWritePlatformServiceImpl.java | 4 +- ...WritePlatformServiceJpaRepositoryImpl.java | 40 +++++++++++++------ 3 files changed, 47 insertions(+), 28 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 b9f8153fc..53a4deab0 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 @@ -1820,24 +1820,11 @@ public class Loan extends AbstractPersistable { validateDisbursementDateIsOnNonWorkingDay(workingDays, allowTransactionsOnNonWorkingDay); validateDisbursementDateIsOnHoliday(allowTransactionsOnHoliday, holidays); - BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName); - boolean isEmiAmountChanged = false; - if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) { - LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(), - actualDisbursementDate.toDate(), emiAmount, this); - this.loanTermVariations.add(loanVariationTerms); - isEmiAmountChanged = true; - } - if (this.repaymentScheduleDetail().isInterestRecalculationEnabled() && (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) { LocalDate recalculateFrom = null; regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom); - } else if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule - || isEmiAmountChanged) { - regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, - workingDays); } updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement()); @@ -1863,6 +1850,24 @@ public class Loan extends AbstractPersistable { } + public void regenerateScheduleOnDisbursement(final LoanScheduleGeneratorFactory loanScheduleFactory, + final ApplicationCurrency currency, final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, + final List holidays, final WorkingDays workingDays, final boolean recalculateSchedule, + final LocalDate actualDisbursementDate, BigDecimal emiAmount) { + boolean isEmiAmountChanged = false; + if (this.loanProduct.isMultiDisburseLoan() && emiAmount != null && emiAmount.compareTo(retriveLastEmiAmount()) != 0) { + LoanTermVariations loanVariationTerms = new LoanTermVariations(LoanTermVariationType.EMI_AMOUNT.getValue(), + actualDisbursementDate.toDate(), emiAmount, this); + this.loanTermVariations.add(loanVariationTerms); + isEmiAmountChanged = true; + } + + if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged) { + regenerateRepaymentSchedule(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, + workingDays); + } + } + public boolean canDisburse(final LocalDate actualDisbursementDate) { Date lastDusburseDate = this.actualDisbursementDate; final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java index 1b45df998..583094ae8 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java @@ -156,7 +156,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat } int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays(); // if (daysToBeAccrued < 1) { return; } - int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays(); +// int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays(); /* * switch (accrualData.getRepaymentFrequency()) { case MONTHS: int * numberOfDaysInMonth = @@ -175,7 +175,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat BigDecimal interestportion = null; BigDecimal feeportion = accrualData.getDueDateFeeIncome(); BigDecimal penaltyportion = accrualData.getDueDatePenaltyIncome(); - if (daysInSchedule >= totalNumberOfDays) { + if (daysToBeAccrued >= totalNumberOfDays) { interestportion = accrualData.getInterestIncome(); } else { double iterest = interestPerDay * daysToBeAccrued; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index e28e38414..b9e3c610c 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -83,6 +83,7 @@ import org.mifosplatform.portfolio.collectionsheet.command.SingleRepaymentComman import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; import org.mifosplatform.portfolio.group.domain.Group; import org.mifosplatform.portfolio.group.exception.GroupNotActiveException; +import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants; import org.mifosplatform.portfolio.loanaccount.command.LoanUpdateCommand; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; @@ -280,7 +281,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf ChangedTransactionDetail changedTransactionDetail = null; if (canDisburse) { Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate); - boolean recalcualteSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal()); + boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal()); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); if (isAccountTransfer) { disburseLoanToSavings(loan, command, disburseAmount, paymentDetail); @@ -301,9 +302,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue()); } + regenerateScheduleOnDisbursement(command, loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, + workingDays, recalculateSchedule); + if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { + this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(), + loan, null); + } changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday, - allowTransactionsOnNonWorkingDay, recalcualteSchedule, restCalendarInstance); + allowTransactionsOnNonWorkingDay, recalculateSchedule, restCalendarInstance); } if (!changes.isEmpty()) { saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -322,10 +329,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); } } - if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { - createLoanScheduleArchive(loan, applicationCurrency, calculatedRepaymentsStartingFromDate, isHolidayEnabled, holidays, - workingDays); - } + postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } @@ -476,7 +480,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (canDisburse) { Money amountBeforeAdjust = loan.getPrincpal(); Money disburseAmount = loan.adjustDisburseAmount(command, actualDisbursementDate); - boolean recalcualteSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal()); + boolean recalculateSchedule = amountBeforeAdjust.isNotEqualTo(loan.getPrincpal()); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull("externalId"); if (isAccountTransfer) { disburseLoanToSavings(loan, command, disburseAmount, paymentDetail); @@ -496,10 +500,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId( loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_DETAIL.getValue()); } - + regenerateScheduleOnDisbursement(command, loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays, + workingDays, recalculateSchedule); + if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { + this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(), + loan, null); + } changedTransactionDetail = loan.disburse(this.loanScheduleFactory, currentUser, command, applicationCurrency, changes, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday, - allowTransactionsOnNonWorkingDay, recalcualteSchedule, restCalendarInstance); + allowTransactionsOnNonWorkingDay, recalculateSchedule, restCalendarInstance); } if (!changes.isEmpty()) { @@ -518,10 +527,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue()); } } - - if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { - createLoanScheduleArchive(loan, applicationCurrency, firstRepaymentOnDate, isHolidayEnabled, holidays, workingDays); - } postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } final Set loanCharges = loan.charges(); @@ -2315,6 +2320,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } + private void regenerateScheduleOnDisbursement(final JsonCommand command, final Loan loan, final ApplicationCurrency applicationCurrency, + final LocalDate calculatedRepaymentsStartingFromDate, final boolean isHolidayEnabled, final List holidays, + final WorkingDays workingDays, final boolean recalculateSchedule) { + final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); + BigDecimal emiAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.emiAmountParameterName); + loan.regenerateScheduleOnDisbursement(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, + isHolidayEnabled, holidays, workingDays, recalculateSchedule, actualDisbursementDate, emiAmount); + } + private List retrieveRepaymentScheduleFromModel(LoanScheduleModel model) { final List installments = new ArrayList<>(); for (final LoanScheduleModelPeriod scheduledLoanInstallment : model.getPeriods()) { From 2e4203614c3cbfdc4339d2c0a96a90552ef8d3fc Mon Sep 17 00:00:00 2001 From: Pramod Nuthakki Date: Thu, 18 Sep 2014 10:09:26 +0530 Subject: [PATCH 4/4] updated API documentation --- api-docs/apiLive.htm | 119 +++++++++++------- .../loanaccount/api/LoansApiResource.java | 3 +- 2 files changed, 77 insertions(+), 45 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index eeafcd99f..ef7cb31a0 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -6819,18 +6819,10 @@ An enumeration that indicates the type of transaction processing strategy to be - recalculationFrequencyType + recalculationRestFrequencyDate - Frequency at which Outstanding Principal is recalculated. This field is mandatory when interest interest recalculation is enabled for product. -
Available calculation types: -
    -
  1. 1 = Same as repayment period
  2. -
  3. 2 = Daily
  4. -
  5. 3 = Weekly
  6. -
  7. 4 = Fortnightly
  8. -
  9. 5 = Monthly
  10. -
+ Specifies rest frequency start date for interest recalculation. This date must be before or equal to disbursement date @@ -7004,7 +6996,12 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1& "id": 2, "code": "loanRescheduleStrategyMethod.reduce.number.of.installments", "value": "Reduce number of installments" - } + }, + "recalculationRestFrequencyType": { + "id":1, + "code":"interestRecalculationFrequencyType.same.as.repayment.period", + "value":"Same as repayment period" + } } "charges": [], "productOptions": [ @@ -7195,32 +7192,7 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1& "fieldOfficerId":0, "currency":{"code":"USD","name":"US Dollar","decimalPlaces":2,"displaySymbol":"$","nameCode":"currency.USD","displayLabel":"US Dollar ($)"} } - ], - "interestRecalculationFrequencyTypeOptions": [{ - "id": 1, - "code": "interestRecalculationFrequencyType.same.as.repayment.period", - "value": "Same as repayment period" - }, - { - "id": 2, - "code": "interestRecalculationFrequencyType.daily", - "value": "Daily" - }, - { - "id": 3, - "code": "interestRecalculationFrequencyType.weekly", - "value": "Weekly" - }, - { - "id": 4, - "code": "interestRecalculationFrequencyType.fortnightly", - "value": "Fortnightly" - }, - { - "id": 5, - "code": "interestRecalculationFrequencyType.monthly", - "value": "Monthly" - }], + ] } @@ -7251,6 +7223,12 @@ GET https://DomainName/api/v1/loans/template?templateType=individual&clientId=1&
'repaymentSchedule': Loan schedule data.
+
+ 'originalSchedule': Loan schedule data without interest recalculations. +
+
+ 'futureSchedule': Loan schedule data from today date(will be displayed only for interest first repayment strategy processors) +
'transactions': Loan transactions data.
@@ -7960,10 +7938,10 @@ Request Body:
- + - +
Additional Mandatory Fields if interest recalculation is enabled for product
Additional Mandatory Fields if interest recalculation is enabled for product and Rest frequency not same as repayment period
recalculationFrequencyTyperecalculationRestFrequencyDate

@@ -8007,7 +7985,6 @@ Request Body: "submittedOnDate": "10 Jun 2013", "linkAccountId" : "1", "fixedEmiAmount":1100, - "recalculationFrequencyType":"1", "maxOutstandingLoanBalance":"35000", "disbursementData":[{"expectedDisbursementDate":"01 November 2013", "principal":22000,"approvedPrincipal":22000}] @@ -10854,6 +10831,25 @@ An enumeration that indicates the type of transaction processing strategy to be Specifies what action should perform on loan repayment schedule for advance payments.
Example Values:1=Reschedule next repayments, 2=Reduce number of installments, 3=Reduce EMI amount + + recalculationRestFrequencyType + + + Specifies effective date from which the late or advanced payment amounts will be considered in recalculation.
+ Example Values:1=Same as repayment period, 2=Daily, 3=Weekly, 4=Monthly + + + recalculationRestFrequencyInterval + + + Specifies rest frequency interval for interest recalculation.
+ + + recalculationRestFrequencyDate + + + Specifies rest frequency start date for interest recalculation.
+ @@ -10963,7 +10959,12 @@ GET https://DomainName/api/v1/loanproducts "id": 2, "code": "loanRescheduleStrategyMethod.reduce.number.of.installments", "value": "Reduce number of installments" - } + }, + "recalculationRestFrequencyType": { + "id":1, + "code":"interestRecalculationFrequencyType.same.as.repayment.period", + "value":"Same as repayment period" + } }, "accountingRule": { "id": 2, @@ -11811,7 +11812,28 @@ GET https://DomainName/api/v1/loanproducts/template "id": 1, "code": "loanRescheduleStrategyMethod.reschedule.next.repayments", "value": "Reschedule next repayments" - }] + }], + "interestRecalculationFrequencyTypeOptions": [ + { + "id":1, + "code":"interestRecalculationFrequencyType.same.as.repayment.period", + "value":"Same as repayment period" + }, + { + "id":2, + "code":"interestRecalculationFrequencyType.daily", + "value":"Daily" + }, + { + "id":3, + "code":"interestRecalculationFrequencyType.weekly", + "value":"Weekly" + }, + { + "id":4, + "code":"interestRecalculationFrequencyType.monthly", + "value":"Monthly" + }] } @@ -12100,7 +12122,15 @@ GET https://DomainName/api/v1/loanproducts/{productId}
Additional Mandatory Fields if interest recalculation is enabled(true)
- interestRecalculationCompoundingMethod,rescheduleStrategyMethod + interestRecalculationCompoundingMethod, rescheduleStrategyMethod, recalculationRestFrequencyType + + + + + + + +
Additional Optional Fields if interest recalculation is enabled(true) and recalculationRestFrequencyType is not same as repayment period
recalculationRestFrequencyInterval, recalculationRestFrequencyDate
@@ -12199,6 +12229,7 @@ Request Body: "isInterestRecalculationEnabled":"true", "interestRecalculationCompoundingMethod":"0", "rescheduleStrategyMethod":"3", + "recalculationRestFrequencyType":"1", "accountingRule":"2", "fundSourceAccountId":"4", "loanPortfolioAccountId":"8", @@ -12321,6 +12352,7 @@ Request Body: "isInterestRecalculationEnabled":"true", "interestRecalculationCompoundingMethod":"0", "rescheduleStrategyMethod":"3", + "recalculationRestFrequencyType":"1" } @@ -12331,6 +12363,7 @@ Request Body: "isInterestRecalculationEnabled":"true", "interestRecalculationCompoundingMethod":"0", "rescheduleStrategyMethod":"3", + "recalculationRestFrequencyType":"1" } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java index fefed4eb7..28b8cac87 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/api/LoansApiResource.java @@ -365,8 +365,7 @@ public class LoansApiResource { this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId); } - if (associationParameters.contains("originalSchedule") && loanBasicDetails.isInterestRecalculationEnabled() - && loanBasicDetails.isInterestRecalculationEnabled()) { + if (associationParameters.contains("originalSchedule") && loanBasicDetails.isInterestRecalculationEnabled()) { mandatoryResponseParameters.add("originalSchedule"); LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule( loanId, repaymentScheduleRelatedData, disbursementData);