MIFOSX-1490 : added interest schedule for interest first repayment strategies
This commit is contained in:
+2
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `m_loan_repayment_schedule`
|
||||||
|
ADD COLUMN `recalculated_interest_component` TINYINT(1) NOT NULL DEFAULT '0';
|
||||||
+43
-3
@@ -8,6 +8,7 @@ package org.mifosplatform.integrationtests;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -15,6 +16,7 @@ import java.util.Calendar;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.joda.time.Days;
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -722,9 +724,28 @@ public class AccountingScenarioIntegrationTest {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
||||||
|
loanID);
|
||||||
// MAKE 1
|
// MAKE 1
|
||||||
final float FIRST_INTEREST = 200.0f;
|
List fromDateList = (List) loanSchedule.get(1).get("fromDate");
|
||||||
final float INTEREST_4_DAYS = 13.33F;
|
LocalDate fromDateLocal = LocalDate.now();
|
||||||
|
fromDateLocal = fromDateLocal.withYear((int) fromDateList.get(0));
|
||||||
|
fromDateLocal = fromDateLocal.withMonthOfYear((int) fromDateList.get(1));
|
||||||
|
fromDateLocal = fromDateLocal.withDayOfMonth((int) fromDateList.get(2));
|
||||||
|
|
||||||
|
List dueDateList = (List) loanSchedule.get(1).get("dueDate");
|
||||||
|
LocalDate dueDateLocal = LocalDate.now();
|
||||||
|
dueDateLocal = dueDateLocal.withYear((int) dueDateList.get(0));
|
||||||
|
dueDateLocal = dueDateLocal.withMonthOfYear((int) dueDateList.get(1));
|
||||||
|
dueDateLocal = dueDateLocal.withDayOfMonth((int) dueDateList.get(2));
|
||||||
|
|
||||||
|
int totalDaysInPeriod = Days.daysBetween(fromDateLocal, dueDateLocal).getDays();
|
||||||
|
|
||||||
|
float totalInterest = (float) loanSchedule.get(1).get("interestOriginalDue");
|
||||||
|
DecimalFormat numberFormat = new DecimalFormat("#.00");
|
||||||
|
float INTEREST_4_DAYS = totalInterest / totalDaysInPeriod * 4;
|
||||||
|
INTEREST_4_DAYS = new Float(numberFormat.format(INTEREST_4_DAYS));
|
||||||
|
|
||||||
this.loanTransactionHelper.checkAccrualTransactionForRepayment(getDateAsLocalDate(currentDate), INTEREST_4_DAYS, FEE_PORTION,
|
this.loanTransactionHelper.checkAccrualTransactionForRepayment(getDateAsLocalDate(currentDate), INTEREST_4_DAYS, FEE_PORTION,
|
||||||
PENALTY_PORTION, loanID);
|
PENALTY_PORTION, loanID);
|
||||||
|
|
||||||
@@ -797,8 +818,27 @@ public class AccountingScenarioIntegrationTest {
|
|||||||
|
|
||||||
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(runOndate);
|
this.periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(runOndate);
|
||||||
|
|
||||||
|
final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec,
|
||||||
|
loanID);
|
||||||
// MAKE 1
|
// MAKE 1
|
||||||
final float INTEREST_3_DAYS = 10.0F;
|
List fromDateList = (List) loanSchedule.get(1).get("fromDate");
|
||||||
|
LocalDate fromDateLocal = LocalDate.now();
|
||||||
|
fromDateLocal = fromDateLocal.withYear((int) fromDateList.get(0));
|
||||||
|
fromDateLocal = fromDateLocal.withMonthOfYear((int) fromDateList.get(1));
|
||||||
|
fromDateLocal = fromDateLocal.withDayOfMonth((int) fromDateList.get(2));
|
||||||
|
|
||||||
|
List dueDateList = (List) loanSchedule.get(1).get("dueDate");
|
||||||
|
LocalDate dueDateLocal = LocalDate.now();
|
||||||
|
dueDateLocal = dueDateLocal.withYear((int) dueDateList.get(0));
|
||||||
|
dueDateLocal = dueDateLocal.withMonthOfYear((int) dueDateList.get(1));
|
||||||
|
dueDateLocal = dueDateLocal.withDayOfMonth((int) dueDateList.get(2));
|
||||||
|
|
||||||
|
int totalDaysInPeriod = Days.daysBetween(fromDateLocal, dueDateLocal).getDays();
|
||||||
|
|
||||||
|
float totalInterest = (float) loanSchedule.get(1).get("interestOriginalDue");
|
||||||
|
DecimalFormat numberFormat = new DecimalFormat("#.00");
|
||||||
|
float INTEREST_3_DAYS = totalInterest / totalDaysInPeriod * 3;
|
||||||
|
INTEREST_3_DAYS = new Float(numberFormat.format(INTEREST_3_DAYS));
|
||||||
this.loanTransactionHelper.checkAccrualTransactionForRepayment(getDateAsLocalDate(runOndate), INTEREST_3_DAYS, FEE_PORTION,
|
this.loanTransactionHelper.checkAccrualTransactionForRepayment(getDateAsLocalDate(runOndate), INTEREST_3_DAYS, FEE_PORTION,
|
||||||
PENALTY_PORTION, loanID);
|
PENALTY_PORTION, loanID);
|
||||||
|
|
||||||
|
|||||||
+28
-20
@@ -977,7 +977,7 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||||
scheduledLoanInstallment.penaltyChargesDue());
|
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent());
|
||||||
addRepaymentScheduleInstallment(installment);
|
addRepaymentScheduleInstallment(installment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3774,15 +3774,17 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
final Set<LoanInstallmentCharge> loanChargePerInstallments = new HashSet<>();
|
final Set<LoanInstallmentCharge> loanChargePerInstallments = new HashSet<>();
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee()) {
|
||||||
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
for (final LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
if (!installment.isRecalculatedInterestComponent()) {
|
||||||
if (loanCharge.getChargeCalculation().isFlat()) {
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
if (loanCharge.getChargeCalculation().isFlat()) {
|
||||||
} else {
|
amount = loanCharge.amount().divide(BigDecimal.valueOf(repaymentScheduleDetail().getNumberOfRepayments()));
|
||||||
amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(), installment)
|
} else {
|
||||||
.getAmount();
|
amount = calculateInstallmentChargeAmount(loanCharge.getChargeCalculation(), loanCharge.getPercentage(),
|
||||||
|
installment).getAmount();
|
||||||
|
}
|
||||||
|
final LoanInstallmentCharge loanInstallmentCharge = new LoanInstallmentCharge(amount, loanCharge, installment);
|
||||||
|
loanChargePerInstallments.add(loanInstallmentCharge);
|
||||||
}
|
}
|
||||||
final LoanInstallmentCharge loanInstallmentCharge = new LoanInstallmentCharge(amount, loanCharge, installment);
|
|
||||||
loanChargePerInstallments.add(loanInstallmentCharge);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loanChargePerInstallments;
|
return loanChargePerInstallments;
|
||||||
@@ -4140,8 +4142,8 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
updateLoanSummaryDerivedFields();
|
updateLoanSummaryDerivedFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Money fetchPrepaymentDetail(final LoanScheduleGeneratorFactory loanScheduleFactory) {
|
public LoanRepaymentScheduleInstallment fetchPrepaymentDetail(final LoanScheduleGeneratorFactory loanScheduleFactory) {
|
||||||
Money amount = Money.zero(getCurrency());
|
LoanRepaymentScheduleInstallment installment = null;
|
||||||
|
|
||||||
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
|
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
|
||||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||||
@@ -4177,21 +4179,27 @@ public class Loan extends AbstractPersistable<Long> {
|
|||||||
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null, getInArrearsTolerance(),
|
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null, getInArrearsTolerance(),
|
||||||
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData,
|
||||||
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
|
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate());
|
||||||
amount = loanScheduleGenerator.fetchPrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(), loanApplicationTerms,
|
installment = loanScheduleGenerator.calculatePrepaymentAmount(this.repaymentScheduleInstallments, getCurrency(),
|
||||||
mc);
|
loanApplicationTerms, LocalDate.now());
|
||||||
} else {
|
} else {
|
||||||
amount = this.getTotalOutstandingOnLoan();
|
installment = this.getTotalOutstandingOnLoan();
|
||||||
}
|
}
|
||||||
return amount;
|
return installment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Money getTotalOutstandingOnLoan() {
|
private LoanRepaymentScheduleInstallment getTotalOutstandingOnLoan() {
|
||||||
Money cumulativeOurstanding = Money.zero(loanCurrency());
|
Money feeCharges = Money.zero(loanCurrency());
|
||||||
|
Money penaltyCharges = Money.zero(loanCurrency());
|
||||||
|
Money totalPrincipal = Money.zero(loanCurrency());
|
||||||
|
Money totalInterest = Money.zero(loanCurrency());
|
||||||
for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
|
||||||
cumulativeOurstanding = cumulativeOurstanding.plus(scheduledRepayment.getTotalOutstanding(getCurrency()));
|
totalPrincipal = totalPrincipal.plus(scheduledRepayment.getPrincipalOutstanding(loanCurrency()));
|
||||||
|
totalInterest = totalInterest.plus(scheduledRepayment.getInterestOutstanding(loanCurrency()));
|
||||||
|
feeCharges = feeCharges.plus(scheduledRepayment.getFeeChargesOutstanding(loanCurrency()));
|
||||||
|
penaltyCharges = penaltyCharges.plus(scheduledRepayment.getPenaltyChargesOutstanding(loanCurrency()));
|
||||||
}
|
}
|
||||||
return cumulativeOurstanding;
|
return new LoanRepaymentScheduleInstallment(null, 0, LocalDate.now(), LocalDate.now(), totalPrincipal.getAmount(),
|
||||||
|
totalInterest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<LoanRepaymentScheduleInstallment> fetchRepaymentScheduleInstallments() {
|
public List<LoanRepaymentScheduleInstallment> fetchRepaymentScheduleInstallments() {
|
||||||
|
|||||||
+13
-1
@@ -99,6 +99,9 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
@Column(name = "obligations_met_on_date")
|
@Column(name = "obligations_met_on_date")
|
||||||
private Date obligationsMetOnDate;
|
private Date obligationsMetOnDate;
|
||||||
|
|
||||||
|
@Column(name = "recalculated_interest_component", nullable = false)
|
||||||
|
private boolean recalculatedInterestComponent;
|
||||||
|
|
||||||
protected LoanRepaymentScheduleInstallment() {
|
protected LoanRepaymentScheduleInstallment() {
|
||||||
this.installmentNumber = null;
|
this.installmentNumber = null;
|
||||||
this.fromDate = null;
|
this.fromDate = null;
|
||||||
@@ -108,7 +111,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
|
|
||||||
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
|
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
|
||||||
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
|
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
|
||||||
final BigDecimal penaltyCharges) {
|
final BigDecimal penaltyCharges, boolean recalculatedInterestComponent) {
|
||||||
this.loan = loan;
|
this.loan = loan;
|
||||||
this.installmentNumber = installmentNumber;
|
this.installmentNumber = installmentNumber;
|
||||||
this.fromDate = fromDate.toDateTimeAtStartOfDay().toDate();
|
this.fromDate = fromDate.toDateTimeAtStartOfDay().toDate();
|
||||||
@@ -118,6 +121,7 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
this.feeChargesCharged = defaultToNullIfZero(feeCharges);
|
this.feeChargesCharged = defaultToNullIfZero(feeCharges);
|
||||||
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
|
this.penaltyCharges = defaultToNullIfZero(penaltyCharges);
|
||||||
this.obligationsMet = false;
|
this.obligationsMet = false;
|
||||||
|
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
|
private BigDecimal defaultToNullIfZero(final BigDecimal value) {
|
||||||
@@ -540,4 +544,12 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
|||||||
public Money getTotalPaidLate(final MonetaryCurrency currency) {
|
public Money getTotalPaidLate(final MonetaryCurrency currency) {
|
||||||
return Money.of(currency, this.totalPaidLate);
|
return Money.of(currency, this.totalPaidLate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isRecalculatedInterestComponent() {
|
||||||
|
return this.recalculatedInterestComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecalculatedInterestComponent(boolean recalculatedInterestComponent) {
|
||||||
|
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+21
-21
@@ -30,18 +30,18 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
for (final LoanRepaymentScheduleInstallment period : repaymentPeriods) {
|
||||||
|
|
||||||
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges,
|
final Money feeChargesDueForRepaymentPeriod = cumulativeFeeChargesDueWithin(startDate, period.getDueDate(), loanCharges,
|
||||||
currency, period, repaymentPeriods.size(), totalInterest);
|
currency, period, repaymentPeriods.size(), totalInterest, !period.isRecalculatedInterestComponent());
|
||||||
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
|
final Money feeChargesWaivedForRepaymentPeriod = cumulativeFeeChargesWaivedWithin(startDate, period.getDueDate(), loanCharges,
|
||||||
currency);
|
currency, !period.isRecalculatedInterestComponent());
|
||||||
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
final Money feeChargesWrittenOffForRepaymentPeriod = cumulativeFeeChargesWrittenOffWithin(startDate, period.getDueDate(),
|
||||||
loanCharges, currency);
|
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||||
|
|
||||||
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(),
|
final Money penaltyChargesDueForRepaymentPeriod = cumulativePenaltyChargesDueWithin(startDate, period.getDueDate(),
|
||||||
loanCharges, currency, period, repaymentPeriods.size(), totalInterest);
|
loanCharges, currency, period, repaymentPeriods.size(), totalInterest, !period.isRecalculatedInterestComponent());
|
||||||
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
|
final Money penaltyChargesWaivedForRepaymentPeriod = cumulativePenaltyChargesWaivedWithin(startDate, period.getDueDate(),
|
||||||
loanCharges, currency);
|
loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||||
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
|
final Money penaltyChargesWrittenOffForRepaymentPeriod = cumulativePenaltyChargesWrittenOffWithin(startDate,
|
||||||
period.getDueDate(), loanCharges, currency);
|
period.getDueDate(), loanCharges, currency, !period.isRecalculatedInterestComponent());
|
||||||
|
|
||||||
period.updateChargePortion(feeChargesDueForRepaymentPeriod, feeChargesWaivedForRepaymentPeriod,
|
period.updateChargePortion(feeChargesDueForRepaymentPeriod, feeChargesWaivedForRepaymentPeriod,
|
||||||
feeChargesWrittenOffForRepaymentPeriod, penaltyChargesDueForRepaymentPeriod, penaltyChargesWaivedForRepaymentPeriod,
|
feeChargesWrittenOffForRepaymentPeriod, penaltyChargesDueForRepaymentPeriod, penaltyChargesWaivedForRepaymentPeriod,
|
||||||
@@ -53,13 +53,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
|
|
||||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||||
final MonetaryCurrency monetaryCurrency, LoanRepaymentScheduleInstallment period, int numberOfRepayments,
|
final MonetaryCurrency monetaryCurrency, LoanRepaymentScheduleInstallment period, int numberOfRepayments,
|
||||||
final Money totalInterest) {
|
final Money totalInterest, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(monetaryCurrency);
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isFeeCharge()) {
|
if (loanCharge.isFeeCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
@@ -75,11 +75,11 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||||
}
|
}
|
||||||
}else if (loanCharge.isOverdueInstallmentCharge()
|
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
}else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
} else if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
@@ -101,13 +101,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Money cumulativeFeeChargesWaivedWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativeFeeChargesWaivedWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency) {
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isFeeCharge()) {
|
if (loanCharge.isFeeCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
if (loanChargePerInstallment != null) {
|
if (loanChargePerInstallment != null) {
|
||||||
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
|
||||||
@@ -122,13 +122,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Money cumulativeFeeChargesWrittenOffWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativeFeeChargesWrittenOffWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency) {
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isFeeCharge()) {
|
if (loanCharge.isFeeCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
if (loanChargePerInstallment != null) {
|
if (loanChargePerInstallment != null) {
|
||||||
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
|
||||||
@@ -144,13 +144,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
|
|
||||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, LoanRepaymentScheduleInstallment period,
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, LoanRepaymentScheduleInstallment period,
|
||||||
int numberOfRepayments, Money totalInterest) {
|
int numberOfRepayments, Money totalInterest, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
@@ -166,7 +166,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
} else {
|
} else {
|
||||||
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
cumulative = cumulative.plus(loanCharge.amount().divide(BigDecimal.valueOf(numberOfRepayments)));
|
||||||
}
|
}
|
||||||
}else if (loanCharge.isOverdueInstallmentCharge()
|
} else if (loanCharge.isOverdueInstallmentCharge()
|
||||||
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
&& loanCharge.isDueForCollectionFromAndUpToAndIncluding(periodStart, periodEnd)
|
||||||
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
&& loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
cumulative = cumulative.plus(loanCharge.chargeAmount());
|
||||||
@@ -192,13 +192,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Money cumulativePenaltyChargesWaivedWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativePenaltyChargesWaivedWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency) {
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
if (loanChargePerInstallment != null) {
|
if (loanChargePerInstallment != null) {
|
||||||
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWaived(currency));
|
||||||
@@ -213,13 +213,13 @@ public class LoanRepaymentScheduleProcessingWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Money cumulativePenaltyChargesWrittenOffWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativePenaltyChargesWrittenOffWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency) {
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency currency, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(currency);
|
Money cumulative = Money.zero(currency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
LoanInstallmentCharge loanChargePerInstallment = loanCharge.getInstallmentLoanCharge(periodEnd);
|
||||||
if (loanChargePerInstallment != null) {
|
if (loanChargePerInstallment != null) {
|
||||||
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
|
cumulative = cumulative.plus(loanChargePerInstallment.getAmountWrittenOff(currency));
|
||||||
|
|||||||
+31
-9
@@ -6,7 +6,6 @@
|
|||||||
package org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor;
|
package org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -26,6 +25,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
|||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.CreocoreLoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
|
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.InterestPrincipalPenaltyFeesOrderLoanRepaymentScheduleTransactionProcessor;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.RecalculationDetail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract implementation of {@link LoanRepaymentScheduleTransactionProcessor}
|
* Abstract implementation of {@link LoanRepaymentScheduleTransactionProcessor}
|
||||||
@@ -438,29 +438,51 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<LocalDate, Money> handleRepaymentSchedule(final List<LoanTransaction> transactionsPostDisbursement,
|
public List<RecalculationDetail> handleRepaymentSchedule(final List<LoanTransaction> transactionsPostDisbursement,
|
||||||
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments,
|
final MonetaryCurrency currency, final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
final LoanRepaymentScheduleInstallment installment, final Map<LocalDate, LocalDate> recalculationDates) {
|
final LoanRepaymentScheduleInstallment installment, final Map<LocalDate, LocalDate> recalculationDates,
|
||||||
Map<LocalDate, Money> unprocessedMap = new HashMap<>();
|
final LoanTransaction preCloseTransaction) {
|
||||||
|
List<RecalculationDetail> earlypaymentDetail = new ArrayList<>();
|
||||||
Money currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
Money currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
||||||
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||||
Money amountToProcess = null;
|
Money amountToProcess = null;
|
||||||
|
LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||||
|
if (loanTransaction != preCloseTransaction) {
|
||||||
|
transactionDate = recalculationDates.get(transactionDate);
|
||||||
|
}
|
||||||
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
newLoanTransaction.resetDerivedComponents();
|
newLoanTransaction.resetDerivedComponents();
|
||||||
Money unProcessed = processTransaction(newLoanTransaction, currency, installments, amountToProcess);
|
Money unProcessed = processTransaction(newLoanTransaction, currency, installments, amountToProcess);
|
||||||
if (loanTransaction.getTransactionDate().isAfter(installment.getFromDate())
|
if (transactionDate.isAfter(installment.getFromDate()) && transactionDate.isBefore(installment.getDueDate())) {
|
||||||
&& recalculationDates.get(loanTransaction.getTransactionDate()).isBefore(installment.getDueDate())) {
|
|
||||||
Money earlyPayment = currentInstallmentOutstanding.minus(installment.getTotalOutstanding(currency));
|
Money earlyPayment = currentInstallmentOutstanding.minus(installment.getTotalOutstanding(currency));
|
||||||
if (earlyPayment.isGreaterThanZero()) {
|
if (earlyPayment.isGreaterThanZero()) {
|
||||||
unprocessedMap.put(loanTransaction.getTransactionDate(), earlyPayment);
|
earlypaymentDetail.add(new RecalculationDetail(false, transactionDate, null, earlyPayment, false));
|
||||||
}
|
}
|
||||||
currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
currentInstallmentOutstanding = installment.getTotalOutstanding(currency);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unProcessed.isGreaterThanZero()) {
|
if (unProcessed.isGreaterThanZero()) {
|
||||||
unprocessedMap.put(loanTransaction.getTransactionDate(), unProcessed);
|
earlypaymentDetail.add(new RecalculationDetail(false, transactionDate, null, unProcessed, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return unprocessedMap;
|
return earlypaymentDetail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInterestFirstRepaymentScheduleTransactionProcessor() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyTransaction(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments) {
|
||||||
|
for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
|
||||||
|
Money amountToProcess = null;
|
||||||
|
final LoanTransaction newLoanTransaction = LoanTransaction.copyTransactionProperties(loanTransaction);
|
||||||
|
newLoanTransaction.resetDerivedComponents();
|
||||||
|
processTransaction(newLoanTransaction, currency, installments, amountToProcess);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+11
-3
@@ -11,11 +11,11 @@ import java.util.Set;
|
|||||||
|
|
||||||
import org.joda.time.LocalDate;
|
import org.joda.time.LocalDate;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
import org.mifosplatform.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||||
|
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.RecalculationDetail;
|
||||||
|
|
||||||
public interface LoanRepaymentScheduleTransactionProcessor {
|
public interface LoanRepaymentScheduleTransactionProcessor {
|
||||||
|
|
||||||
@@ -28,8 +28,16 @@ public interface LoanRepaymentScheduleTransactionProcessor {
|
|||||||
void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency,
|
void handleWriteOff(LoanTransaction loanTransaction, MonetaryCurrency loanCurrency,
|
||||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments);
|
||||||
|
|
||||||
Map<LocalDate, Money> handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
List<RecalculationDetail> handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment,
|
List<LoanRepaymentScheduleInstallment> installments, LoanRepaymentScheduleInstallment currentInstallment,
|
||||||
Map<LocalDate, LocalDate> recalculationDates);
|
Map<LocalDate, LocalDate> recalculationDates, LoanTransaction preCloseTransaction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in interest recalculation to introduce new interest only installment.
|
||||||
|
*/
|
||||||
|
boolean isInterestFirstRepaymentScheduleTransactionProcessor();
|
||||||
|
|
||||||
|
void applyTransaction(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments);
|
||||||
|
|
||||||
}
|
}
|
||||||
+5
@@ -204,4 +204,9 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
|
|||||||
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
protected void onLoanOverpayment(final LoanTransaction loanTransaction, final Money loanOverPaymentAmount) {
|
||||||
// dont do anything for with loan over-payment
|
// dont do anything for with loan over-payment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInterestFirstRepaymentScheduleTransactionProcessor() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+415
-106
@@ -97,6 +97,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
LocalDate periodStartDateApplicableForInterest = periodStartDate;
|
LocalDate periodStartDateApplicableForInterest = periodStartDate;
|
||||||
|
|
||||||
int periodNumber = 1;
|
int periodNumber = 1;
|
||||||
|
int instalmentNumber = 1;
|
||||||
Money totalCumulativePrincipal = principalDisbursed.zero();
|
Money totalCumulativePrincipal = principalDisbursed.zero();
|
||||||
Money totalCumulativeInterest = principalDisbursed.zero();
|
Money totalCumulativeInterest = principalDisbursed.zero();
|
||||||
Money totalOutstandingInterestPaymentDueToGrace = principalDisbursed.zero();
|
Money totalOutstandingInterestPaymentDueToGrace = principalDisbursed.zero();
|
||||||
@@ -109,12 +110,43 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
Money reducePrincipal = totalCumulativePrincipal.zero();
|
Money reducePrincipal = totalCumulativePrincipal.zero();
|
||||||
Money fixedEmiAmount = totalCumulativePrincipal.zero();
|
Money fixedEmiAmount = totalCumulativePrincipal.zero();
|
||||||
|
int daysCalcForInstallmentNumber = 0;
|
||||||
|
LocalDate scheduleStartDateAsPerFrequency = periodStartDate;
|
||||||
while (!outstandingBalance.isZero()) {
|
while (!outstandingBalance.isZero()) {
|
||||||
isFirstRepayment = false;
|
isFirstRepayment = false;
|
||||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
// to insert a new schedule between actual schedule to collect
|
||||||
isFirstRepayment);
|
// interest(till the transaction date) first on a payment
|
||||||
LocalDate scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
boolean recalculatedInterestComponent = false;
|
||||||
isHolidayEnabled, holidays, workingDays);
|
RecalculationDetail interestonlyPeriodDetail = null;
|
||||||
|
if (diffAmt != null) {
|
||||||
|
for (RecalculationDetail recalculationDetail : diffAmt) {
|
||||||
|
if (recalculationDetail.isInterestompound() && recalculationDetail.getStartDate().isEqual(periodStartDate)) {
|
||||||
|
interestonlyPeriodDetail = recalculationDetail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LocalDate scheduledDueDate = null;
|
||||||
|
LocalDate scheduledDueDateAsPerFrequency = null;
|
||||||
|
|
||||||
|
int daysInPeriodApplicableInFullInstallment = 0;
|
||||||
|
// will change the schedule dates as per the interest only Period
|
||||||
|
if (interestonlyPeriodDetail == null) {
|
||||||
|
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||||
|
isFirstRepayment);
|
||||||
|
scheduledDueDate = this.scheduledDateGenerator.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||||
|
isHolidayEnabled, holidays, workingDays);
|
||||||
|
scheduledDueDateAsPerFrequency = scheduledDueDate;
|
||||||
|
} else {
|
||||||
|
recalculatedInterestComponent = true;
|
||||||
|
scheduledDueDate = interestonlyPeriodDetail.getToDate();
|
||||||
|
daysCalcForInstallmentNumber = periodNumber;
|
||||||
|
LocalDate actualRepayment = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate,
|
||||||
|
loanApplicationTerms, isFirstRepayment);
|
||||||
|
scheduledDueDateAsPerFrequency = this.scheduledDateGenerator.adjustRepaymentDate(actualRepayment, loanApplicationTerms,
|
||||||
|
isHolidayEnabled, holidays, workingDays);
|
||||||
|
}
|
||||||
|
|
||||||
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
|
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
|
||||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||||
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
||||||
@@ -141,10 +173,27 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDateApplicableForInterest, scheduledDueDate).getDays();
|
daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDateApplicableForInterest, scheduledDueDate).getDays();
|
||||||
}
|
}
|
||||||
|
|
||||||
final double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
// this block identifies full payment period as per the
|
||||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, scheduledDueDate,
|
// schedule(excluding interest payments) for interest calculation
|
||||||
loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(),
|
daysInPeriodApplicableInFullInstallment = daysInPeriodApplicableForInterest;
|
||||||
loanApplicationTerms.getRepaymentEvery());
|
if (daysCalcForInstallmentNumber == periodNumber) {
|
||||||
|
periodStartDateApplicableForInterest = scheduleStartDateAsPerFrequency;
|
||||||
|
if (scheduleStartDateAsPerFrequency.isBefore(idealDisbursementDate)) {
|
||||||
|
if (loanApplicationTerms.getInterestChargedFromLocalDate() != null) {
|
||||||
|
periodStartDateApplicableForInterest = loanApplicationTerms.getInterestChargedFromLocalDate();
|
||||||
|
} else {
|
||||||
|
periodStartDateApplicableForInterest = idealDisbursementDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
daysInPeriodApplicableInFullInstallment = Days.daysBetween(periodStartDateApplicableForInterest,
|
||||||
|
scheduledDueDateAsPerFrequency).getDays();
|
||||||
|
}
|
||||||
|
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
||||||
|
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
|
||||||
|
scheduledDueDateAsPerFrequency, loanApplicationTerms.getInterestChargedFromLocalDate(),
|
||||||
|
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
|
||||||
|
|
||||||
Money balanceForcalculation = outstandingBalance;
|
Money balanceForcalculation = outstandingBalance;
|
||||||
// reduce principal is the early payment, will processed as per the
|
// reduce principal is the early payment, will processed as per the
|
||||||
// reschedule strategy
|
// reschedule strategy
|
||||||
@@ -177,10 +226,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5 determine principal,interest of repayment period
|
// 5 determine principal,interest of repayment period
|
||||||
final PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
|
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal,
|
||||||
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
totalCumulativeInterest, totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace,
|
||||||
daysInPeriodApplicableForInterest, balanceForcalculation, loanApplicationTerms, periodNumber, mc);
|
daysInPeriodApplicableInFullInstallment, balanceForcalculation, loanApplicationTerms, periodNumber, mc);
|
||||||
|
|
||||||
if (loanApplicationTerms.getFixedEmiAmount() != null
|
if (loanApplicationTerms.getFixedEmiAmount() != null
|
||||||
&& loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) != 1) {
|
&& loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) != 1) {
|
||||||
@@ -190,17 +239,34 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
// update cumulative fields for principal & interest
|
// update cumulative fields for principal & interest
|
||||||
Money interestForThisinstallment = principalInterestForThisPeriod.interest();
|
Money interestForThisinstallment = principalInterestForThisPeriod.interest();
|
||||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(principalInterestForThisPeriod.principal());
|
if (daysCalcForInstallmentNumber == periodNumber) {
|
||||||
|
interestForThisinstallment = interestForThisinstallment.zero().plus(
|
||||||
|
calculateInterestForDays(daysInPeriodApplicableInFullInstallment, interestForThisinstallment,
|
||||||
|
daysInPeriodApplicableForInterest));
|
||||||
|
}
|
||||||
|
Money principalForThisPeriod = principalDisbursed.zero();
|
||||||
|
Money outstandingForThePeriod = outstandingBalance;
|
||||||
|
// Exclude principal portion for interest only installments
|
||||||
|
if (!recalculatedInterestComponent) {
|
||||||
|
principalForThisPeriod = principalInterestForThisPeriod.principal();
|
||||||
|
if (daysCalcForInstallmentNumber == periodNumber && loanApplicationTerms.getAmortizationMethod().isEqualInstallment()) {
|
||||||
|
principalForThisPeriod = principalForThisPeriod.plus(principalInterestForThisPeriod.interest()).minus(
|
||||||
|
interestForThisinstallment);
|
||||||
|
}
|
||||||
|
totalCumulativePrincipal = totalCumulativePrincipal.plus(principalForThisPeriod);
|
||||||
|
|
||||||
|
// 6. update outstandingLoanBlance using correct 'principalDue'
|
||||||
|
outstandingBalance = outstandingBalance.minus(principalForThisPeriod);
|
||||||
|
}
|
||||||
|
|
||||||
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
|
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
|
||||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||||
|
|
||||||
// 6. update outstandingLoanBlance using correct 'principalDue'
|
|
||||||
outstandingBalance = outstandingBalance.minus(principalInterestForThisPeriod.principal());
|
|
||||||
Money actualOutstandingbalance = outstandingBalance;
|
Money actualOutstandingbalance = outstandingBalance;
|
||||||
|
|
||||||
Money feeChargesForInstallment = principalDisbursed.zero();
|
Money feeChargesForInstallment = principalDisbursed.zero();
|
||||||
Money penaltyChargesForInstallment = principalDisbursed.zero();
|
Money penaltyChargesForInstallment = principalDisbursed.zero();
|
||||||
Money principalForThisPeriod = principalInterestForThisPeriod.principal();
|
|
||||||
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
|
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
|
||||||
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
|
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
|
||||||
reducePrincipal = reducePrincipal.zero();
|
reducePrincipal = reducePrincipal.zero();
|
||||||
@@ -213,51 +279,87 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
|
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Money extraPrincipal = reducePrincipal.zero();
|
||||||
// this block is to identify interest based on late/early payment
|
// this block is to identify interest based on late/early payment
|
||||||
if (diffAmt != null && !diffAmt.isEmpty()) {
|
if (diffAmt != null && !diffAmt.isEmpty()) {
|
||||||
BigDecimal interestDueToLatePayment = BigDecimal.ZERO;
|
BigDecimal interestDueToLatePayment = BigDecimal.ZERO;
|
||||||
BigDecimal interestReducedDueToEarlyPayment = BigDecimal.ZERO;
|
BigDecimal interestReducedDueToEarlyPayment = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
boolean isperiodPrincipalReduced = false;
|
||||||
for (RecalculationDetail detail : diffAmt) {
|
for (RecalculationDetail detail : diffAmt) {
|
||||||
// will increase the principal portion and reduces interest
|
if (!detail.isInterestompound()) {
|
||||||
// as per the number of days. and also identifies reduce
|
// will increase the principal portion and reduces
|
||||||
// principal for reschedule strategy
|
// interest
|
||||||
if (!detail.isLatePayment() && detail.getStartDate().isAfter(periodStartDate)
|
// as per the number of days. and also identifies reduce
|
||||||
&& !detail.getStartDate().isAfter(scheduledDueDate)) {
|
// principal for reschedule strategy
|
||||||
reducePrincipal = reducePrincipal.plus(detail.getAmount());
|
if (!detail.isLatePayment() && detail.getStartDate().isAfter(periodStartDate)
|
||||||
int diffDays = Days.daysBetween(detail.getStartDate(), scheduledDueDate).getDays();
|
&& !detail.getStartDate().isAfter(scheduledDueDate)) {
|
||||||
if (diffDays > 0) {
|
reducePrincipal = reducePrincipal.plus(detail.getAmount());
|
||||||
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
|
extraPrincipal = extraPrincipal.plus(detail.getAmount());
|
||||||
|
int diffDays = Days.daysBetween(detail.getStartDate(), scheduledDueDate).getDays();
|
||||||
|
if (diffDays > 0 && !isperiodPrincipalReduced) {
|
||||||
|
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
|
||||||
|
isperiodPrincipalReduced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate startDate = detail.getStartDate();
|
||||||
|
if (loanApplicationTerms.getInterestChargedFromLocalDate() != null
|
||||||
|
&& startDate.isBefore(loanApplicationTerms.getInterestChargedFromLocalDate())) {
|
||||||
|
startDate = loanApplicationTerms.getInterestChargedFromLocalDate();
|
||||||
|
}
|
||||||
|
LocalDate endDate = scheduledDueDate;
|
||||||
|
if (startDate.isBefore(endDate)) {
|
||||||
|
Money amountForInterestCalculation = detail.getAmount();
|
||||||
|
Money balanceDiff = outstandingForThePeriod.minus(extraPrincipal);
|
||||||
|
if (balanceDiff.isLessThanZero()) {
|
||||||
|
amountForInterestCalculation = amountForInterestCalculation.plus(balanceDiff);
|
||||||
|
}
|
||||||
|
interestReducedDueToEarlyPayment = interestReducedDueToEarlyPayment.add(calculateInterestForSpecificDays(
|
||||||
|
mc, loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace,
|
||||||
|
daysInPeriodApplicableInFullInstallment, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||||
|
amountForInterestCalculation, startDate, endDate));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (reducePrincipal.isLessThanZero()) {
|
// calculates the interest for late payment and increase
|
||||||
reducePrincipal = reducePrincipal.zero();
|
// the interest for the installment
|
||||||
} else {
|
else if (detail.isLatePayment() && detail.isOverlapping(periodStartDate, scheduledDueDate)
|
||||||
Money actualOutstanding = outstandingBalance.minus(reducePrincipal);
|
&& periodStartDate.isBefore(LocalDate.now())) {
|
||||||
principalForThisPeriod = reducePrincipal.plus(principalForThisPeriod);
|
LocalDate fromDate = periodStartDate;
|
||||||
if (actualOutstanding.isLessThanZero()) {
|
LocalDate toDate = scheduledDueDate;
|
||||||
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
|
if (!detail.getStartDate().isBefore(periodStartDate)) {
|
||||||
|
fromDate = detail.getStartDate();
|
||||||
|
}
|
||||||
|
if (!detail.getToDate().isAfter(scheduledDueDate)) {
|
||||||
|
toDate = detail.getToDate();
|
||||||
|
}
|
||||||
|
if (toDate.isAfter(LocalDate.now())) {
|
||||||
|
toDate = LocalDate.now();
|
||||||
|
}
|
||||||
|
if (fromDate.isAfter(LocalDate.now())) {
|
||||||
|
fromDate = LocalDate.now();
|
||||||
|
}
|
||||||
|
if (loanApplicationTerms.getInterestChargedFromLocalDate() != null
|
||||||
|
&& fromDate.isBefore(loanApplicationTerms.getInterestChargedFromLocalDate())) {
|
||||||
|
fromDate = loanApplicationTerms.getInterestChargedFromLocalDate();
|
||||||
|
}
|
||||||
|
if (fromDate.isBefore(toDate)) {
|
||||||
|
interestDueToLatePayment = interestDueToLatePayment.add(calculateInterestForSpecificDays(mc,
|
||||||
|
loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace,
|
||||||
|
daysInPeriodApplicableInFullInstallment, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||||
|
detail.getAmount(), fromDate, toDate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
interestReducedDueToEarlyPayment = interestReducedDueToEarlyPayment.add(loanApplicationTerms.interestRateFor(
|
|
||||||
this.paymentPeriodsInOneYearCalculator, mc, detail.getAmount(), detail.getStartDate(), scheduledDueDate));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// calculates the interest for late payment and increase
|
}
|
||||||
// the interest for the installment
|
|
||||||
else if (detail.isLatePayment() && detail.isOverlapping(periodStartDate, scheduledDueDate)
|
if (reducePrincipal.isLessThanZero()) {
|
||||||
&& periodStartDate.isBefore(LocalDate.now())) {
|
reducePrincipal = reducePrincipal.zero();
|
||||||
LocalDate fromDate = periodStartDate;
|
} else {
|
||||||
LocalDate toDate = scheduledDueDate;
|
Money actualOutstanding = outstandingBalance.minus(reducePrincipal);
|
||||||
if (!detail.getStartDate().isBefore(periodStartDate)) {
|
principalForThisPeriod = reducePrincipal.plus(principalForThisPeriod);
|
||||||
fromDate = detail.getStartDate();
|
if (actualOutstanding.isLessThanZero()) {
|
||||||
}
|
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
|
||||||
if (!detail.getToDate().isAfter(scheduledDueDate)) {
|
|
||||||
toDate = detail.getToDate();
|
|
||||||
}
|
|
||||||
if (toDate.isAfter(LocalDate.now())) {
|
|
||||||
toDate = LocalDate.now();
|
|
||||||
}
|
|
||||||
interestDueToLatePayment = interestDueToLatePayment.add(loanApplicationTerms.interestRateFor(
|
|
||||||
this.paymentPeriodsInOneYearCalculator, mc, detail.getAmount(), fromDate, toDate));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,9 +383,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
.plus(penaltyChargesForInstallment);
|
.plus(penaltyChargesForInstallment);
|
||||||
|
|
||||||
// 9. create repayment period from parts
|
// 9. create repayment period from parts
|
||||||
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, periodStartDate,
|
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, periodStartDate,
|
||||||
scheduledDueDate, principalForThisPeriod, actualOutstandingbalance, interestForThisinstallment,
|
scheduledDueDate, principalForThisPeriod, actualOutstandingbalance, interestForThisinstallment,
|
||||||
feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue);
|
feeChargesForInstallment, penaltyChargesForInstallment, totalInstallmentDue, recalculatedInterestComponent);
|
||||||
periods.add(installment);
|
periods.add(installment);
|
||||||
|
|
||||||
// handle cumulative fields
|
// handle cumulative fields
|
||||||
@@ -296,7 +398,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
periodStartDate = scheduledDueDate;
|
periodStartDate = scheduledDueDate;
|
||||||
periodStartDateApplicableForInterest = periodStartDate;
|
periodStartDateApplicableForInterest = periodStartDate;
|
||||||
|
|
||||||
periodNumber++;
|
if (!recalculatedInterestComponent) {
|
||||||
|
periodNumber++;
|
||||||
|
scheduleStartDateAsPerFrequency = periodStartDate;
|
||||||
|
}
|
||||||
|
instalmentNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse)) {
|
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse)) {
|
||||||
@@ -311,10 +417,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
Money.of(currency, loanScheduleModelPeriod.interestDue()), null);
|
Money.of(currency, loanScheduleModelPeriod.interestDue()), null);
|
||||||
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
Money feeChargesForInstallment = cumulativeFeeChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||||
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||||
totalCumulativeInterest, numberOfRepayments);
|
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent());
|
||||||
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
Money penaltyChargesForInstallment = cumulativePenaltyChargesDueWithin(loanScheduleModelPeriod.periodFromDate(),
|
||||||
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
loanScheduleModelPeriod.periodDueDate(), loanCharges, currency, principalInterest, principalDisbursed,
|
||||||
totalCumulativeInterest, numberOfRepayments);
|
totalCumulativeInterest, numberOfRepayments, !loanScheduleModelPeriod.isRecalculatedInterestComponent());
|
||||||
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
totalFeeChargesCharged = totalFeeChargesCharged.add(feeChargesForInstallment.getAmount());
|
||||||
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
totalPenaltyChargesCharged = totalPenaltyChargesCharged.add(penaltyChargesForInstallment.getAmount());
|
||||||
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount());
|
loanScheduleModelPeriod.addLoanCharges(feeChargesForInstallment.getAmount(), penaltyChargesForInstallment.getAmount());
|
||||||
@@ -324,14 +430,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
// this block is to add extra re-payment schedules with interest portion
|
// this block is to add extra re-payment schedules with interest portion
|
||||||
// if the last payment is missed
|
// if the last payment is missed
|
||||||
if (diffAmt != null && !diffAmt.isEmpty() && !periodStartDate.isAfter(LocalDate.now())) {
|
if (diffAmt != null && !diffAmt.isEmpty() && !periodStartDate.isAfter(LocalDate.now())) {
|
||||||
|
boolean recalculatedInterestComponent = true;
|
||||||
Map<LocalDate, RecalculationDetail> processDetails = new TreeMap<>();
|
Map<LocalDate, RecalculationDetail> processDetails = new TreeMap<>();
|
||||||
for (RecalculationDetail detail : diffAmt) {
|
for (RecalculationDetail detail : diffAmt) {
|
||||||
if (!periodStartDate.isAfter(detail.getStartDate()) && detail.isLatePayment()) {
|
if (!periodStartDate.isAfter(detail.getStartDate()) && detail.isLatePayment() && !detail.isInterestompound()) {
|
||||||
if (processDetails.containsKey(detail.getToDate())) {
|
if (processDetails.containsKey(detail.getToDate())) {
|
||||||
RecalculationDetail recalculationDetail = processDetails.get(detail.getToDate());
|
RecalculationDetail recalculationDetail = processDetails.get(detail.getToDate());
|
||||||
RecalculationDetail updatedDetail = new RecalculationDetail(recalculationDetail.isLatePayment(),
|
RecalculationDetail updatedDetail = new RecalculationDetail(recalculationDetail.isLatePayment(),
|
||||||
recalculationDetail.getStartDate(), recalculationDetail.getToDate(), recalculationDetail.getAmount().plus(
|
recalculationDetail.getStartDate(), recalculationDetail.getToDate(), recalculationDetail.getAmount().plus(
|
||||||
detail.getAmount()));
|
detail.getAmount()), detail.isInterestompound());
|
||||||
processDetails.put(updatedDetail.getToDate(), updatedDetail);
|
processDetails.put(updatedDetail.getToDate(), updatedDetail);
|
||||||
} else {
|
} else {
|
||||||
processDetails.put(detail.getToDate(), detail);
|
processDetails.put(detail.getToDate(), detail);
|
||||||
@@ -350,11 +457,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalInterestCharged = totalInterestCharged.add(interestDueToLatePayment.getAmount());
|
totalInterestCharged = totalInterestCharged.add(interestDueToLatePayment.getAmount());
|
||||||
totalRepaymentExpected = totalRepaymentExpected.add(interestDueToLatePayment.getAmount());
|
totalRepaymentExpected = totalRepaymentExpected.add(interestDueToLatePayment.getAmount());
|
||||||
|
|
||||||
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate,
|
final LoanScheduleModelPeriod installment = LoanScheduleModelRepaymentPeriod.repayment(instalmentNumber, fromDate,
|
||||||
toDate, interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment,
|
toDate, interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment,
|
||||||
interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment);
|
interestDueToLatePayment.zero(), interestDueToLatePayment.zero(), interestDueToLatePayment,
|
||||||
|
recalculatedInterestComponent);
|
||||||
periods.add(installment);
|
periods.add(installment);
|
||||||
periodNumber++;
|
instalmentNumber++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,6 +473,23 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
totalOutstanding);
|
totalOutstanding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BigDecimal calculateInterestForSpecificDays(final MathContext mc, final LoanApplicationTerms loanApplicationTerms,
|
||||||
|
int periodNumber, Money totalOutstandingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest,
|
||||||
|
double interestCalculationGraceOnRepaymentPeriodFraction, Money amount, LocalDate startDate, LocalDate endDate) {
|
||||||
|
PrincipalInterest principalInterest = loanApplicationTerms.calculateTotalInterestForPeriod(this.paymentPeriodsInOneYearCalculator,
|
||||||
|
interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc, totalOutstandingInterestPaymentDueToGrace.zero(),
|
||||||
|
daysInPeriodApplicableForInterest, amount);
|
||||||
|
|
||||||
|
int days = Days.daysBetween(startDate, endDate).getDays();
|
||||||
|
double interest = calculateInterestForDays(daysInPeriodApplicableForInterest, principalInterest.interest(), days);
|
||||||
|
|
||||||
|
return BigDecimal.valueOf(interest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double calculateInterestForDays(int daysInPeriodApplicableForInterest, Money interest, int days) {
|
||||||
|
return ((interest.getAmount().doubleValue()) / daysInPeriodApplicableForInterest) * days;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
|
public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
|
||||||
double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
|
double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
|
||||||
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest,
|
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, int daysInPeriodApplicableForInterest,
|
||||||
@@ -432,13 +557,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
private Money cumulativeFeeChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd, final Set<LoanCharge> loanCharges,
|
||||||
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod,
|
final MonetaryCurrency monetaryCurrency, final PrincipalInterest principalInterestForThisPeriod,
|
||||||
final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments) {
|
final Money principalDisbursed, final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments,
|
||||||
|
boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(monetaryCurrency);
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isFeeCharge()) {
|
if (loanCharge.isFeeCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
@@ -482,13 +608,13 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
private Money cumulativePenaltyChargesDueWithin(final LocalDate periodStart, final LocalDate periodEnd,
|
||||||
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency,
|
final Set<LoanCharge> loanCharges, final MonetaryCurrency monetaryCurrency,
|
||||||
final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
|
final PrincipalInterest principalInterestForThisPeriod, final Money principalDisbursed,
|
||||||
final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments) {
|
final Money totalInterestChargedForFullLoanTerm, int numberOfRepayments, boolean isInstallmentChargeApplicable) {
|
||||||
|
|
||||||
Money cumulative = Money.zero(monetaryCurrency);
|
Money cumulative = Money.zero(monetaryCurrency);
|
||||||
|
|
||||||
for (final LoanCharge loanCharge : loanCharges) {
|
for (final LoanCharge loanCharge : loanCharges) {
|
||||||
if (loanCharge.isPenaltyCharge()) {
|
if (loanCharge.isPenaltyCharge()) {
|
||||||
if (loanCharge.isInstalmentFee()) {
|
if (loanCharge.isInstalmentFee() && isInstallmentChargeApplicable) {
|
||||||
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
if (loanCharge.getChargeCalculation().isPercentageBased()) {
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
if (loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest()) {
|
||||||
@@ -544,9 +670,11 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
LoanScheduleModel loanScheduleModel = null;
|
LoanScheduleModel loanScheduleModel = null;
|
||||||
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
List<LoanRepaymentScheduleInstallment> installments = previousSchedule;
|
||||||
List<LoanRepaymentScheduleInstallment> removeInstallments = new ArrayList<>();
|
List<LoanRepaymentScheduleInstallment> removeInstallments = new ArrayList<>();
|
||||||
|
MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
|
||||||
|
LoanTransaction preCloseTransaction = getPreclosureTransaction(transactions, installments, currency, loanApplicationTerms);
|
||||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
if (!loanApplicationTerms.isPrincipalGraceApplicableForThisPeriod(installment.getInstallmentNumber())
|
if (installment.isRecalculatedInterestComponent() && recalculateFrom != null
|
||||||
&& installment.getPrincipal(loanApplicationTerms.getCurrency()).isZero()) {
|
&& !recalculateFrom.isAfter(installment.getDueDate())) {
|
||||||
removeInstallments.add(installment);
|
removeInstallments.add(installment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -556,22 +684,129 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
workingDays);
|
workingDays);
|
||||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recalculateFrom == null) {
|
if (recalculateFrom == null) {
|
||||||
recalculateFrom = loanApplicationTerms.getExpectedDisbursementDate();
|
recalculateFrom = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final List<RecalculationDetail> recalculationDetails = new ArrayList<>();
|
||||||
|
final Map<LocalDate, RecalculationDetail> retainRecalculationDetails = new HashMap<>();
|
||||||
|
LocalDate processTransactionsForInterestCompound = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
|
while (recalculateFrom.isAfter(processTransactionsForInterestCompound) && !transactions.isEmpty()
|
||||||
|
&& loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()) {
|
||||||
|
|
||||||
|
int installmentNumber = findLastProcessedInstallmentNumber(installments, processTransactionsForInterestCompound);
|
||||||
|
List<LoanTransaction> processTransactions = processTransactions(transactions, processTransactionsForInterestCompound);
|
||||||
|
if (processTransactions.size() > 0) {
|
||||||
|
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||||
|
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||||
|
loanScheduleModel, installments, currency, recalculationDetails, retainRecalculationDetails, false,
|
||||||
|
installmentNumber, processTransactions, preCloseTransaction);
|
||||||
|
}
|
||||||
|
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||||
|
processTransactionsForInterestCompound = getNextRecalculateFromDate(transactions, processTransactionsForInterestCompound);
|
||||||
|
}
|
||||||
|
|
||||||
while (!recalculateFrom.isAfter(LocalDate.now())) {
|
while (!recalculateFrom.isAfter(LocalDate.now())) {
|
||||||
|
|
||||||
int installmentNumber = findLastProcessedInstallmentNumber(installments, recalculateFrom);
|
int installmentNumber = findLastProcessedInstallmentNumber(installments, recalculateFrom);
|
||||||
List<LoanTransaction> processTransactions = processTransactions(transactions, recalculateFrom);
|
List<LoanTransaction> processTransactions = processTransactions(transactions, recalculateFrom);
|
||||||
|
if (loanRepaymentScheduleTransactionProcessor.isInterestFirstRepaymentScheduleTransactionProcessor()
|
||||||
|
&& processTransactions.size() > 0) {
|
||||||
|
loanScheduleModel = createInterestOnlyRecalculationDetails(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||||
|
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||||
|
loanScheduleModel, installments, currency, recalculationDetails, retainRecalculationDetails, true,
|
||||||
|
installmentNumber, processTransactions, preCloseTransaction);
|
||||||
|
if (loanScheduleModel != null) {
|
||||||
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||||
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled,
|
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges, isHolidayEnabled,
|
||||||
holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule, loanScheduleModel, installments,
|
||||||
processTransactions, installmentNumber);
|
processTransactions, installmentNumber, recalculationDetails, preCloseTransaction);
|
||||||
|
if (loanScheduleModel != null) {
|
||||||
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
|
}
|
||||||
recalculateFrom = getNextRecalculateFromDate(transactions, recalculateFrom);
|
recalculateFrom = getNextRecalculateFromDate(transactions, recalculateFrom);
|
||||||
}
|
}
|
||||||
return loanScheduleModel;
|
return loanScheduleModel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method identifies interest only repayment periods
|
||||||
|
*/
|
||||||
|
private LoanScheduleModel createInterestOnlyRecalculationDetails(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||||
|
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||||
|
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||||
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
|
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||||
|
final List<RecalculationDetail> recalculationDetails, final Map<LocalDate, RecalculationDetail> retainRecalculationDetails,
|
||||||
|
boolean recalculate, int installmentNumber, List<LoanTransaction> processTransactions, LoanTransaction preCloseTransaction) {
|
||||||
|
LoanTransaction loanTransaction = processTransactions.remove(processTransactions.size() - 1);
|
||||||
|
if (loanTransaction == preCloseTransaction) {
|
||||||
|
processTransactions.add(loanTransaction);
|
||||||
|
return loanScheduleModel;
|
||||||
|
}
|
||||||
|
LocalDate processTransactionsForInterestCompound = getNextRestScheduleDate(loanTransaction.getTransactionDate().minusDays(1),
|
||||||
|
loanApplicationTerms, isHolidayEnabled, holidays, workingDays);
|
||||||
|
List<LoanRepaymentScheduleInstallment> scheduleInstallments = getInstallmentsForInterestCompound(installments,
|
||||||
|
processTransactionsForInterestCompound, retainRecalculationDetails.keySet());
|
||||||
|
|
||||||
|
if (!scheduleInstallments.isEmpty()) {
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
installment.resetDerivedComponents();
|
||||||
|
installment.updateDerivedFields(currency, loanApplicationTerms.getExpectedDisbursementDate());
|
||||||
|
}
|
||||||
|
loanRepaymentScheduleTransactionProcessor.applyTransaction(processTransactions, currency, installments);
|
||||||
|
|
||||||
|
Money principalUnprocessed = Money.zero(currency);
|
||||||
|
Money interestUnprocessed = Money.zero(currency);
|
||||||
|
Money feeUnprocessed = Money.zero(currency);
|
||||||
|
boolean isBeforeFirstInstallment = false;
|
||||||
|
if (scheduleInstallments.size() == 1) {
|
||||||
|
LoanRepaymentScheduleInstallment installment = scheduleInstallments.get(0);
|
||||||
|
if (installment.getInstallmentNumber() == 1
|
||||||
|
&& (installment.getDueDate().isAfter(loanTransaction.getTransactionDate()) || (installment
|
||||||
|
.isRecalculatedInterestComponent() && installment.getDueDate()
|
||||||
|
.isEqual(loanTransaction.getTransactionDate())))) {
|
||||||
|
isBeforeFirstInstallment = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isBeforeFirstInstallment) {
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : scheduleInstallments) {
|
||||||
|
principalUnprocessed = principalUnprocessed.plus(installment.getPrincipalOutstanding(currency));
|
||||||
|
interestUnprocessed = interestUnprocessed.plus(installment.getInterestOutstanding(currency));
|
||||||
|
feeUnprocessed = feeUnprocessed.plus(installment.getFeeChargesOutstanding(currency));
|
||||||
|
feeUnprocessed = feeUnprocessed.plus(installment.getPenaltyChargesOutstanding(currency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (interestUnprocessed.isLessThan(loanTransaction.getAmount(currency))) {
|
||||||
|
LoanRepaymentScheduleInstallment lastProcessedInstallment = scheduleInstallments.get(scheduleInstallments.size() - 1);
|
||||||
|
LocalDate startDate = lastProcessedInstallment.getDueDate();
|
||||||
|
if (isBeforeFirstInstallment) {
|
||||||
|
startDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||||
|
}
|
||||||
|
RecalculationDetail recalculationDetail = new RecalculationDetail(false, startDate, processTransactionsForInterestCompound,
|
||||||
|
null, true);
|
||||||
|
retainRecalculationDetails.put(processTransactionsForInterestCompound, recalculationDetail);
|
||||||
|
recalculationDetails.add(recalculationDetail);
|
||||||
|
if (recalculate) {
|
||||||
|
recalculationDetails.retainAll(retainRecalculationDetails.values());
|
||||||
|
loanScheduleModel = recalculateInstallment(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||||
|
isHolidayEnabled, holidays, workingDays, loanRepaymentScheduleTransactionProcessor, previousSchedule,
|
||||||
|
loanScheduleModel, installments, processTransactions, installmentNumber, recalculationDetails,
|
||||||
|
preCloseTransaction);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processTransactions.add(loanTransaction);
|
||||||
|
return loanScheduleModel;
|
||||||
|
}
|
||||||
|
|
||||||
private LocalDate getNextRecalculateFromDate(List<LoanTransaction> processTransactions, LocalDate preCalculationDate) {
|
private LocalDate getNextRecalculateFromDate(List<LoanTransaction> processTransactions, LocalDate preCalculationDate) {
|
||||||
LocalDate recalculateFrom = null;
|
LocalDate recalculateFrom = null;
|
||||||
for (LoanTransaction loanTransaction : processTransactions) {
|
for (LoanTransaction loanTransaction : processTransactions) {
|
||||||
@@ -590,25 +825,28 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
* Method calls regenerate schedule for a particular set of transactions
|
* Method calls regenerate schedule for a particular set of transactions
|
||||||
* till all the installments are processed to identify early or late
|
* till all the installments are processed to identify early or late
|
||||||
* payments and will be used in regeneration of schedule
|
* payments and will be used in regeneration of schedule
|
||||||
|
*
|
||||||
|
* @param preCloseTransaction
|
||||||
|
* TODO
|
||||||
*/
|
*/
|
||||||
private LoanScheduleModel recalculateInstallment(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private LoanScheduleModel recalculateInstallment(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays,
|
final List<Holiday> holidays, final WorkingDays workingDays,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
final List<LoanRepaymentScheduleInstallment> previousSchedule, LoanScheduleModel loanScheduleModel,
|
||||||
List<LoanRepaymentScheduleInstallment> installments, List<LoanTransaction> processTransactions, final int skipRecalculation) {
|
List<LoanRepaymentScheduleInstallment> installments, List<LoanTransaction> processTransactions, final int skipRecalculation,
|
||||||
|
final List<RecalculationDetail> recalculationDetails, LoanTransaction preCloseTransaction) {
|
||||||
int processInstallmentsFrom = 0;
|
int processInstallmentsFrom = 0;
|
||||||
Integer numberOfInstallments = previousSchedule.size() - 1;
|
Integer lastInstallmentNumber = previousSchedule.size();
|
||||||
final List<RecalculationDetail> recalculationDetails = new ArrayList<>();
|
while (processInstallmentsFrom < lastInstallmentNumber) {
|
||||||
while (processInstallmentsFrom < numberOfInstallments) {
|
|
||||||
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
RecalculatedSchedule recalculatedSchedule = recalculateInterest(mc, applicationCurrency, loanApplicationTerms, loanCharges,
|
||||||
isHolidayEnabled, holidays, workingDays, processTransactions, loanRepaymentScheduleTransactionProcessor,
|
isHolidayEnabled, holidays, workingDays, processTransactions, loanRepaymentScheduleTransactionProcessor,
|
||||||
processInstallmentsFrom, installments, recalculationDetails, skipRecalculation);
|
processInstallmentsFrom, installments, recalculationDetails, skipRecalculation, preCloseTransaction);
|
||||||
processInstallmentsFrom = recalculatedSchedule.getInstallmentNumber();
|
processInstallmentsFrom = recalculatedSchedule.getInstallmentNumber();
|
||||||
if (recalculatedSchedule.getLoanScheduleModel() != null) {
|
if (recalculatedSchedule.getLoanScheduleModel() != null) {
|
||||||
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
loanScheduleModel = recalculatedSchedule.getLoanScheduleModel();
|
||||||
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
installments = retrieveRepaymentSchedule(loanScheduleModel);
|
||||||
numberOfInstallments = installments.size() - 1;
|
lastInstallmentNumber = installments.size() - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loanScheduleModel;
|
return loanScheduleModel;
|
||||||
@@ -616,13 +854,16 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Method identifies late or early payments for schedule recalculation.
|
* Method identifies late or early payments for schedule recalculation.
|
||||||
|
*
|
||||||
|
* @param preCloseTransaction
|
||||||
|
* TODO
|
||||||
*/
|
*/
|
||||||
private RecalculatedSchedule recalculateInterest(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
private RecalculatedSchedule recalculateInterest(final MathContext mc, final ApplicationCurrency applicationCurrency,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
final LoanApplicationTerms loanApplicationTerms, final Set<LoanCharge> loanCharges, final boolean isHolidayEnabled,
|
||||||
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
final List<Holiday> holidays, final WorkingDays workingDays, final List<LoanTransaction> transactions,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final int installmentNumber,
|
||||||
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
final List<LoanRepaymentScheduleInstallment> installments, final List<RecalculationDetail> recalculationDetails,
|
||||||
final int skipRecalculation) {
|
final int skipRecalculation, LoanTransaction preCloseTransaction) {
|
||||||
boolean processRecalculate = false;
|
boolean processRecalculate = false;
|
||||||
int processedInstallmentNumber = installmentNumber;
|
int processedInstallmentNumber = installmentNumber;
|
||||||
|
|
||||||
@@ -642,9 +883,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processedInstallmentNumber = installment.getInstallmentNumber();
|
processedInstallmentNumber = installment.getInstallmentNumber();
|
||||||
if (installment.getDueDate().isAfter(LocalDate.now())) {
|
if (installment.getInstallmentNumber() == installments.size()) {
|
||||||
continue;
|
processRecalculate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<LoanTransaction> transactionsForInstallment = new ArrayList<>();
|
List<LoanTransaction> transactionsForInstallment = new ArrayList<>();
|
||||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||||
LocalDate transactionsDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
|
LocalDate transactionsDate = getNextRestScheduleDate(installment.getDueDate().minusDays(1), loanApplicationTerms,
|
||||||
@@ -659,19 +901,18 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
workingDays));
|
workingDays));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<LocalDate, Money> earlyPaymentMap = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(
|
|
||||||
transactionsForInstallment, currency, processinstallmets, installment, recalculationDates);
|
if (installment.isRecalculatedInterestComponent() && installment.getPrincipal(currency).isGreaterThanZero()) {
|
||||||
|
diffAmt.add(new RecalculationDetail(false, installment.getDueDate(), null, installment.getPrincipal(currency), false));
|
||||||
|
}
|
||||||
|
List<RecalculationDetail> earlypaymentDetail = loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(
|
||||||
|
transactionsForInstallment, currency, processinstallmets, installment, recalculationDates, preCloseTransaction);
|
||||||
|
|
||||||
// this block is to create early payment entries for schedule
|
// this block is to create early payment entries for schedule
|
||||||
// generation
|
// generation
|
||||||
for (Map.Entry<LocalDate, Money> entry : earlyPaymentMap.entrySet()) {
|
for (RecalculationDetail recalculationDetail : earlypaymentDetail) {
|
||||||
LocalDate startDate = entry.getKey();
|
if (!recalculationDetail.getStartDate().isAfter(installment.getDueDate())
|
||||||
if (unpaidPricipal.isGreaterThan(entry.getValue())) {
|
&& recalculationDetail.getStartDate().isAfter(installment.getFromDate())) {
|
||||||
startDate = getNextRestScheduleDate(startDate.minusDays(1), loanApplicationTerms, isHolidayEnabled, holidays,
|
|
||||||
workingDays);
|
|
||||||
}
|
|
||||||
RecalculationDetail recalculationDetail = new RecalculationDetail(false, startDate, null, entry.getValue());
|
|
||||||
if (!entry.getKey().isAfter(installment.getDueDate()) && entry.getKey().isAfter(installment.getFromDate())) {
|
|
||||||
diffAmt.add(recalculationDetail);
|
diffAmt.add(recalculationDetail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -692,7 +933,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
startDate = startDate.minusDays(1);
|
startDate = startDate.minusDays(1);
|
||||||
}
|
}
|
||||||
applyRest(transactions, startDate, recalculateFrom, installment, loanRepaymentScheduleTransactionProcessor, currency,
|
applyRest(transactions, startDate, recalculateFrom, installment, loanRepaymentScheduleTransactionProcessor, currency,
|
||||||
loanApplicationTerms, isHolidayEnabled, holidays, workingDays, installments);
|
loanApplicationTerms, isHolidayEnabled, holidays, workingDays, installments, preCloseTransaction);
|
||||||
totalOutstanding = installment.getTotalOutstanding(currency);
|
totalOutstanding = installment.getTotalOutstanding(currency);
|
||||||
if (totalOutstanding.isGreaterThanZero()) {
|
if (totalOutstanding.isGreaterThanZero()) {
|
||||||
Money latepaymentoutstanding = installment.getPrincipalOutstanding(currency);
|
Money latepaymentoutstanding = installment.getPrincipalOutstanding(currency);
|
||||||
@@ -714,7 +955,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RecalculationDetail recalculationDetail = new RecalculationDetail(true, recalculateFrom, recalcualteTill,
|
RecalculationDetail recalculationDetail = new RecalculationDetail(true, recalculateFrom, recalcualteTill,
|
||||||
latepaymentoutstanding);
|
latepaymentoutstanding, false);
|
||||||
diffAmt.add(recalculationDetail);
|
diffAmt.add(recalculationDetail);
|
||||||
}
|
}
|
||||||
startDate = recalculateFrom.plusDays(1);
|
startDate = recalculateFrom.plusDays(1);
|
||||||
@@ -722,8 +963,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!diffAmt.isEmpty() && skipRecalculation <= installment.getInstallmentNumber())
|
if (!diffAmt.isEmpty() && skipRecalculation <= installment.getInstallmentNumber()) {
|
||||||
|| installment.getInstallmentNumber() == installments.size() - 1) {
|
|
||||||
processRecalculate = true;
|
processRecalculate = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -746,7 +986,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||||
scheduledLoanInstallment.penaltyChargesDue());
|
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent());
|
||||||
installments.add(installment);
|
installments.add(installment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,12 +996,15 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
/**
|
/**
|
||||||
* Method to identify which transaction did the payment for current
|
* Method to identify which transaction did the payment for current
|
||||||
* installment
|
* installment
|
||||||
|
*
|
||||||
|
* @param preCloseTransaction
|
||||||
|
* TODO
|
||||||
*/
|
*/
|
||||||
private void applyRest(List<LoanTransaction> loanTransactions, LocalDate from, LocalDate to,
|
private void applyRest(List<LoanTransaction> loanTransactions, LocalDate from, LocalDate to,
|
||||||
LoanRepaymentScheduleInstallment installment,
|
LoanRepaymentScheduleInstallment installment,
|
||||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, MonetaryCurrency currency,
|
||||||
final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
final LoanApplicationTerms loanApplicationTerms, final boolean isHolidayEnabled, final List<Holiday> holidays,
|
||||||
final WorkingDays workingDays, final List<LoanRepaymentScheduleInstallment> installments) {
|
final WorkingDays workingDays, final List<LoanRepaymentScheduleInstallment> installments, LoanTransaction preCloseTransaction) {
|
||||||
List<LoanTransaction> transactions = new ArrayList<>();
|
List<LoanTransaction> transactions = new ArrayList<>();
|
||||||
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
Map<LocalDate, LocalDate> recalculationDates = new HashMap<>();
|
||||||
for (LoanTransaction transaction : loanTransactions) {
|
for (LoanTransaction transaction : loanTransactions) {
|
||||||
@@ -776,7 +1019,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
}
|
}
|
||||||
|
|
||||||
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(transactions, currency, installments, installment,
|
loanRepaymentScheduleTransactionProcessor.handleRepaymentSchedule(transactions, currency, installments, installment,
|
||||||
recalculationDates);
|
recalculationDates, preCloseTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@@ -796,29 +1039,54 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
* Method returns the amount payable to close the loan account as of today.
|
* Method returns the amount payable to close the loan account as of today.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Money fetchPrepaymentAmount(final List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
public LoanRepaymentScheduleInstallment calculatePrepaymentAmount(final List<LoanRepaymentScheduleInstallment> installments,
|
||||||
final LoanApplicationTerms applicationTerms, final MathContext mc) {
|
MonetaryCurrency currency, final LoanApplicationTerms applicationTerms, final LocalDate onDate) {
|
||||||
Money prepaymentAmount = Money.zero(currency);
|
Money outstandingPrincipal = Money.zero(currency);
|
||||||
Money amount = Money.zero(currency);
|
Money feeCharges = Money.zero(currency);
|
||||||
LocalDate calculateInterestFrom = LocalDate.now();
|
Money penaltyCharges = Money.zero(currency);
|
||||||
|
Money totalPrincipal = Money.zero(currency);
|
||||||
|
Money totalInterest = Money.zero(currency);
|
||||||
|
LocalDate calculateInterestFrom = onDate;
|
||||||
|
LocalDate dueDate = onDate;
|
||||||
|
Money inerestForCurrentInstallment = null;
|
||||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||||
if (currentInstallment.isNotFullyPaidOff()) {
|
if (currentInstallment.isNotFullyPaidOff()) {
|
||||||
if (!currentInstallment.getDueDate().isAfter(LocalDate.now())) {
|
if (!currentInstallment.getDueDate().isAfter(onDate)) {
|
||||||
amount = amount.plus(currentInstallment.getTotalOutstanding(currency));
|
totalPrincipal = totalPrincipal.plus(currentInstallment.getPrincipalOutstanding(currency));
|
||||||
|
totalInterest = totalInterest.plus(currentInstallment.getInterestOutstanding(currency));
|
||||||
|
feeCharges = feeCharges.plus(currentInstallment.getFeeChargesOutstanding(currency));
|
||||||
|
penaltyCharges = penaltyCharges.plus(currentInstallment.getPenaltyChargesOutstanding(currency));
|
||||||
} else {
|
} else {
|
||||||
prepaymentAmount = prepaymentAmount.plus(currentInstallment.getPrincipalOutstanding(currency));
|
outstandingPrincipal = outstandingPrincipal.plus(currentInstallment.getPrincipal(currency));
|
||||||
|
totalPrincipal = totalPrincipal.minus(currentInstallment.getPrincipalCompleted(currency));
|
||||||
|
totalInterest = totalInterest.minus(currentInstallment.getInterestPaid(currency)).minus(
|
||||||
|
currentInstallment.getInterestWaived(currency));
|
||||||
if (currentInstallment.getFromDate().isBefore(calculateInterestFrom)) {
|
if (currentInstallment.getFromDate().isBefore(calculateInterestFrom)) {
|
||||||
calculateInterestFrom = currentInstallment.getFromDate();
|
calculateInterestFrom = currentInstallment.getFromDate();
|
||||||
|
dueDate = currentInstallment.getDueDate();
|
||||||
|
inerestForCurrentInstallment = currentInstallment.getInterestCharged(currency);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (applicationTerms.getInterestChargedFromLocalDate() != null
|
||||||
|
&& calculateInterestFrom.isBefore(applicationTerms.getInterestChargedFromLocalDate())) {
|
||||||
|
calculateInterestFrom = applicationTerms.getInterestChargedFromLocalDate();
|
||||||
|
}
|
||||||
|
Money interest = Money.zero(currency);
|
||||||
|
;
|
||||||
|
if (calculateInterestFrom.isBefore(onDate)) {
|
||||||
|
int daysInPeriodApplicableForInterest = Days.daysBetween(calculateInterestFrom, dueDate).getDays();
|
||||||
|
int days = Days.daysBetween(calculateInterestFrom, onDate).getDays();
|
||||||
|
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, inerestForCurrentInstallment, days));
|
||||||
|
}
|
||||||
|
|
||||||
BigDecimal interest = applicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc, prepaymentAmount,
|
totalInterest = totalInterest.plus(interest);
|
||||||
calculateInterestFrom, LocalDate.now());
|
totalPrincipal = totalPrincipal.plus(outstandingPrincipal);
|
||||||
prepaymentAmount = prepaymentAmount.plus(interest).plus(amount);
|
|
||||||
return prepaymentAmount;
|
return new LoanRepaymentScheduleInstallment(null, 0, onDate, onDate, totalPrincipal.getAmount(), totalInterest.getAmount(),
|
||||||
|
feeCharges.getAmount(), penaltyCharges.getAmount(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer findLastProcessedInstallmentNumber(final List<LoanRepaymentScheduleInstallment> installments, LocalDate date) {
|
private Integer findLastProcessedInstallmentNumber(final List<LoanRepaymentScheduleInstallment> installments, LocalDate date) {
|
||||||
@@ -832,6 +1100,27 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
return installmentNumber;
|
return installmentNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<LoanRepaymentScheduleInstallment> getInstallmentsForInterestCompound(
|
||||||
|
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate date, Collection<LocalDate> processedDates) {
|
||||||
|
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = new ArrayList<>();
|
||||||
|
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||||
|
if (installment.getDueDate().isEqual(date)
|
||||||
|
&& (!installment.isRecalculatedInterestComponent() || (processedDates.contains(date) && installment
|
||||||
|
.isRecalculatedInterestComponent()))) {
|
||||||
|
repaymentScheduleInstallments.clear();
|
||||||
|
break;
|
||||||
|
} else if (installment.getDueDate().isBefore(date)) {
|
||||||
|
repaymentScheduleInstallments.add(installment);
|
||||||
|
} else {
|
||||||
|
if (installment.getInstallmentNumber() == 1) {
|
||||||
|
repaymentScheduleInstallments.add(installment);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return repaymentScheduleInstallments;
|
||||||
|
}
|
||||||
|
|
||||||
private List<LoanTransaction> processTransactions(final List<LoanTransaction> transactions, final LocalDate tillDate) {
|
private List<LoanTransaction> processTransactions(final List<LoanTransaction> transactions, final LocalDate tillDate) {
|
||||||
List<LoanTransaction> toProcess = new ArrayList<>();
|
List<LoanTransaction> toProcess = new ArrayList<>();
|
||||||
for (LoanTransaction loanTransaction : transactions) {
|
for (LoanTransaction loanTransaction : transactions) {
|
||||||
@@ -842,4 +1131,24 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
|||||||
return toProcess;
|
return toProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoanTransaction getPreclosureTransaction(List<LoanTransaction> loanTransactions,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency, final LoanApplicationTerms applicationTerms) {
|
||||||
|
LoanTransaction precloseTransaction = null;
|
||||||
|
for (LoanTransaction loanTransaction : loanTransactions) {
|
||||||
|
if (precloseTransaction == null || precloseTransaction.getTransactionDate().isBefore(loanTransaction.getTransactionDate())) {
|
||||||
|
precloseTransaction = loanTransaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (precloseTransaction != null) {
|
||||||
|
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = calculatePrepaymentAmount(installments, currency,
|
||||||
|
applicationTerms, precloseTransaction.getTransactionDate());
|
||||||
|
if (precloseTransaction.getAmount(currency).isLessThan(loanRepaymentScheduleInstallment.getTotalOutstanding(currency))) {
|
||||||
|
precloseTransaction = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return precloseTransaction;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+17
@@ -236,6 +236,23 @@ public final class LoanApplicationTerms {
|
|||||||
interestRecalculationCompoundingMethod, restCalendarInstance);
|
interestRecalculationCompoundingMethod, restCalendarInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||||
|
final LoanApplicationTerms applicationTerms) {
|
||||||
|
return new LoanApplicationTerms(applicationTerms.currency, applicationTerms.loanTermFrequency,
|
||||||
|
applicationTerms.loanTermPeriodFrequencyType, applicationTerms.numberOfRepayments, repaymentEvery,
|
||||||
|
repaymentPeriodFrequencyType, applicationTerms.amortizationMethod, applicationTerms.interestMethod,
|
||||||
|
applicationTerms.interestRatePerPeriod, applicationTerms.interestRatePeriodFrequencyType,
|
||||||
|
applicationTerms.annualNominalInterestRate, applicationTerms.interestCalculationPeriodMethod, applicationTerms.principal,
|
||||||
|
applicationTerms.expectedDisbursementDate, applicationTerms.repaymentsStartingFromDate,
|
||||||
|
applicationTerms.calculatedRepaymentsStartingFromDate, applicationTerms.principalGrace,
|
||||||
|
applicationTerms.interestPaymentGrace, applicationTerms.interestChargingGrace, applicationTerms.interestChargedFromDate,
|
||||||
|
applicationTerms.inArrearsTolerance, applicationTerms.multiDisburseLoan, applicationTerms.fixedEmiAmount,
|
||||||
|
applicationTerms.disbursementDatas, applicationTerms.maxOutstandingBalance, applicationTerms.emiAmountVariations,
|
||||||
|
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
|
||||||
|
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
|
||||||
|
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance);
|
||||||
|
}
|
||||||
|
|
||||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||||
final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
|
final PeriodFrequencyType repaymentPeriodFrequencyType, final AmortizationMethod amortizationMethod,
|
||||||
|
|||||||
+2
-3
@@ -13,7 +13,6 @@ import org.joda.time.LocalDate;
|
|||||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
|
||||||
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
import org.mifosplatform.organisation.workingdays.domain.WorkingDays;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||||
@@ -31,6 +30,6 @@ public interface LoanScheduleGenerator {
|
|||||||
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
List<LoanRepaymentScheduleInstallment> previousSchedule, LocalDate recalculateFrom);
|
||||||
|
|
||||||
Money fetchPrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
LoanRepaymentScheduleInstallment calculatePrepaymentAmount(List<LoanRepaymentScheduleInstallment> installments, MonetaryCurrency currency,
|
||||||
LoanApplicationTerms applicationTerms, MathContext mc);
|
LoanApplicationTerms applicationTerms, LocalDate onDate);
|
||||||
}
|
}
|
||||||
+5
@@ -94,4 +94,9 @@ public final class LoanScheduleModelDisbursementPeriod implements LoanScheduleMo
|
|||||||
public void addLoanCharges(@SuppressWarnings("unused") BigDecimal feeCharge, @SuppressWarnings("unused") BigDecimal penaltyCharge) {
|
public void addLoanCharges(@SuppressWarnings("unused") BigDecimal feeCharge, @SuppressWarnings("unused") BigDecimal penaltyCharge) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecalculatedInterestComponent() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
@@ -31,4 +31,6 @@ public interface LoanScheduleModelPeriod {
|
|||||||
BigDecimal penaltyChargesDue();
|
BigDecimal penaltyChargesDue();
|
||||||
|
|
||||||
void addLoanCharges(BigDecimal feeCharge, BigDecimal penaltyCharge);
|
void addLoanCharges(BigDecimal feeCharge, BigDecimal penaltyCharge);
|
||||||
|
|
||||||
|
boolean isRecalculatedInterestComponent();
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -26,18 +26,19 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
|||||||
private Money feeChargesDue;
|
private Money feeChargesDue;
|
||||||
private Money penaltyChargesDue;
|
private Money penaltyChargesDue;
|
||||||
private Money totalDue;
|
private Money totalDue;
|
||||||
|
private final boolean recalculatedInterestComponent;
|
||||||
|
|
||||||
public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate,
|
public static LoanScheduleModelRepaymentPeriod repayment(final int periodNumber, final LocalDate startDate,
|
||||||
final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
|
final LocalDate scheduledDueDate, final Money principalDue, final Money outstandingLoanBalance, final Money interestDue,
|
||||||
final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue) {
|
final Money feeChargesDue, final Money penaltyChargesDue, final Money totalDue, boolean recalculatedInterestComponent) {
|
||||||
|
|
||||||
return new LoanScheduleModelRepaymentPeriod(periodNumber, startDate, scheduledDueDate, principalDue, outstandingLoanBalance,
|
return new LoanScheduleModelRepaymentPeriod(periodNumber, startDate, scheduledDueDate, principalDue, outstandingLoanBalance,
|
||||||
interestDue, feeChargesDue, penaltyChargesDue, totalDue);
|
interestDue, feeChargesDue, penaltyChargesDue, totalDue, recalculatedInterestComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoanScheduleModelRepaymentPeriod(final int periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
public LoanScheduleModelRepaymentPeriod(final int periodNumber, final LocalDate fromDate, final LocalDate dueDate,
|
||||||
final Money principalDue, final Money outstandingLoanBalance, final Money interestDue, final Money feeChargesDue,
|
final Money principalDue, final Money outstandingLoanBalance, final Money interestDue, final Money feeChargesDue,
|
||||||
final Money penaltyChargesDue, final Money totalDue) {
|
final Money penaltyChargesDue, final Money totalDue, final boolean recalculatedInterestComponent) {
|
||||||
this.periodNumber = periodNumber;
|
this.periodNumber = periodNumber;
|
||||||
this.fromDate = fromDate;
|
this.fromDate = fromDate;
|
||||||
this.dueDate = dueDate;
|
this.dueDate = dueDate;
|
||||||
@@ -47,6 +48,7 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
|||||||
this.feeChargesDue = feeChargesDue;
|
this.feeChargesDue = feeChargesDue;
|
||||||
this.penaltyChargesDue = penaltyChargesDue;
|
this.penaltyChargesDue = penaltyChargesDue;
|
||||||
this.totalDue = totalDue;
|
this.totalDue = totalDue;
|
||||||
|
this.recalculatedInterestComponent = recalculatedInterestComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,4 +124,9 @@ public final class LoanScheduleModelRepaymentPeriod implements LoanScheduleModel
|
|||||||
this.penaltyChargesDue = this.penaltyChargesDue.plus(penaltyCharge);
|
this.penaltyChargesDue = this.penaltyChargesDue.plus(penaltyCharge);
|
||||||
this.totalDue = this.totalDue.plus(feeCharge).plus(penaltyCharge);
|
this.totalDue = this.totalDue.plus(feeCharge).plus(penaltyCharge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecalculatedInterestComponent() {
|
||||||
|
return this.recalculatedInterestComponent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+8
-2
@@ -11,15 +11,18 @@ import org.mifosplatform.organisation.monetary.domain.Money;
|
|||||||
public class RecalculationDetail {
|
public class RecalculationDetail {
|
||||||
|
|
||||||
private boolean isLatePayment;
|
private boolean isLatePayment;
|
||||||
|
private boolean isInterestompound;
|
||||||
private LocalDate startDate;
|
private LocalDate startDate;
|
||||||
private LocalDate toDate;
|
private LocalDate toDate;
|
||||||
private Money amount;
|
private Money amount;
|
||||||
|
|
||||||
public RecalculationDetail(final boolean isLatePayment, final LocalDate startDate, final LocalDate toDate, final Money amount) {
|
public RecalculationDetail(final boolean isLatePayment, final LocalDate startDate, final LocalDate toDate, final Money amount,
|
||||||
|
final boolean isInterestompound) {
|
||||||
this.isLatePayment = isLatePayment;
|
this.isLatePayment = isLatePayment;
|
||||||
this.startDate = startDate;
|
this.startDate = startDate;
|
||||||
this.toDate = toDate;
|
this.toDate = toDate;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
|
this.isInterestompound = isInterestompound;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLatePayment() {
|
public boolean isLatePayment() {
|
||||||
@@ -42,8 +45,11 @@ public class RecalculationDetail {
|
|||||||
return !this.startDate.isAfter(endDate) && !startDate.isAfter(this.toDate);
|
return !this.startDate.isAfter(endDate) && !startDate.isAfter(this.toDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updateAmount(Money amount) {
|
public void updateAmount(Money amount) {
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInterestompound() {
|
||||||
|
return this.isInterestompound;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-52
@@ -24,8 +24,6 @@ import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
|||||||
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
||||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||||
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
||||||
import org.mifosplatform.portfolio.common.domain.DaysInMonthType;
|
|
||||||
import org.mifosplatform.portfolio.common.domain.DaysInYearType;
|
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
|
||||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionType;
|
||||||
@@ -108,7 +106,9 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
if (accruredTill == null || lastLoanId == null || !lastLoanId.equals(accrualData.getLoanId())) {
|
if (accruredTill == null || lastLoanId == null || !lastLoanId.equals(accrualData.getLoanId())) {
|
||||||
accruredTill = accrualData.getAccruedTill();
|
accruredTill = accrualData.getAccruedTill();
|
||||||
}
|
}
|
||||||
addAccrualTillSpecificDate(tilldate, accrualData, accruredTill);
|
if (accruredTill == null || accruredTill.isBefore(tilldate)) {
|
||||||
|
addAccrualTillSpecificDate(tilldate, accrualData, accruredTill);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
addAccrualAccounting(accrualData);
|
addAccrualAccounting(accrualData);
|
||||||
accruredTill = accrualData.getDueDateAsLocaldate();
|
accruredTill = accrualData.getDueDateAsLocaldate();
|
||||||
@@ -131,10 +131,10 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanScheduleAccrualData accrualData,
|
private void addAccrualTillSpecificDate(final LocalDate tilldate, final LoanScheduleAccrualData accrualData,
|
||||||
final LocalDate accruredTill) throws Exception {
|
final LocalDate accruredTill) throws Exception {
|
||||||
LocalDate interestStartDate = accrualData.getFromDateAsLocaldate();
|
LocalDate interestStartDate = accrualData.getFromDateAsLocaldate();
|
||||||
boolean isInterestStartDateEqulsInstallmentStartdate = true;
|
// boolean isInterestStartDateEqulsInstallmentStartdate = true;
|
||||||
if (accrualData.getInterestCalculatedFrom() != null
|
if (accrualData.getInterestCalculatedFrom() != null
|
||||||
&& accrualData.getFromDateAsLocaldate().isBefore(accrualData.getInterestCalculatedFrom())) {
|
&& accrualData.getFromDateAsLocaldate().isBefore(accrualData.getInterestCalculatedFrom())) {
|
||||||
isInterestStartDateEqulsInstallmentStartdate = false;
|
// isInterestStartDateEqulsInstallmentStartdate = false;
|
||||||
if (accrualData.getInterestCalculatedFrom().isBefore(accrualData.getDueDateAsLocaldate())) {
|
if (accrualData.getInterestCalculatedFrom().isBefore(accrualData.getDueDateAsLocaldate())) {
|
||||||
interestStartDate = accrualData.getInterestCalculatedFrom();
|
interestStartDate = accrualData.getInterestCalculatedFrom();
|
||||||
} else {
|
} else {
|
||||||
@@ -157,22 +157,18 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays();
|
int daysToBeAccrued = Days.daysBetween(startDate, tilldate).getDays();
|
||||||
// if (daysToBeAccrued < 1) { return; }
|
// if (daysToBeAccrued < 1) { return; }
|
||||||
int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays();
|
int daysInSchedule = Days.daysBetween(interestStartDate, tilldate).getDays();
|
||||||
switch (accrualData.getRepaymentFrequency()) {
|
/*
|
||||||
case MONTHS:
|
* switch (accrualData.getRepaymentFrequency()) { case MONTHS: int
|
||||||
int numberOfDaysInMonth = calculateTotalNumberOfDaysForMonth(totalNumberOfDays, accrualData);
|
* numberOfDaysInMonth =
|
||||||
if (!(!isInterestStartDateEqulsInstallmentStartdate && totalNumberOfDays < numberOfDaysInMonth)) {
|
* calculateTotalNumberOfDaysForMonth(totalNumberOfDays, accrualData);
|
||||||
totalNumberOfDays = numberOfDaysInMonth;
|
* if (!(!isInterestStartDateEqulsInstallmentStartdate &&
|
||||||
}
|
* totalNumberOfDays < numberOfDaysInMonth)) { totalNumberOfDays =
|
||||||
break;
|
* numberOfDaysInMonth; } break; case YEARS: int numberOfDaysinYear =
|
||||||
case YEARS:
|
* calculateTotalNumberOfDaysForYear(totalNumberOfDays, accrualData); if
|
||||||
int numberOfDaysinYear = calculateTotalNumberOfDaysForYear(totalNumberOfDays, accrualData);
|
* (!(!isInterestStartDateEqulsInstallmentStartdate && totalNumberOfDays
|
||||||
if (!(!isInterestStartDateEqulsInstallmentStartdate && totalNumberOfDays < numberOfDaysinYear)) {
|
* < numberOfDaysinYear)) { totalNumberOfDays = numberOfDaysinYear; }
|
||||||
totalNumberOfDays = numberOfDaysinYear;
|
* break; default: break; }
|
||||||
}
|
*/
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
double interestPerDay = accrualData.getInterestIncome().doubleValue() / totalNumberOfDays;
|
double interestPerDay = accrualData.getInterestIncome().doubleValue() / totalNumberOfDays;
|
||||||
BigDecimal amount = BigDecimal.ZERO;
|
BigDecimal amount = BigDecimal.ZERO;
|
||||||
@@ -232,37 +228,23 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int calculateTotalNumberOfDaysForMonth(final int totalNumberOfDays, final LoanScheduleAccrualData accrualData) {
|
/*
|
||||||
int numberOfDays = totalNumberOfDays;
|
* private int calculateTotalNumberOfDaysForMonth(final int
|
||||||
DaysInMonthType type = DaysInMonthType.fromInt(accrualData.getNumberOfDaysInMonth());
|
* totalNumberOfDays, final LoanScheduleAccrualData accrualData) { int
|
||||||
switch (type) {
|
* numberOfDays = totalNumberOfDays; DaysInMonthType type =
|
||||||
case DAYS_30:
|
* DaysInMonthType.fromInt(accrualData.getNumberOfDaysInMonth()); switch
|
||||||
numberOfDays = 30 * accrualData.getRepayEvery();
|
* (type) { case DAYS_30: numberOfDays = 30 * accrualData.getRepayEvery();
|
||||||
break;
|
* break; default: break; } return numberOfDays; }
|
||||||
default:
|
*
|
||||||
break;
|
* private int calculateTotalNumberOfDaysForYear(final int
|
||||||
}
|
* totalNumberOfDays, final LoanScheduleAccrualData accrualData) { int
|
||||||
return numberOfDays;
|
* numberOfDays = totalNumberOfDays; DaysInYearType type =
|
||||||
}
|
* DaysInYearType.fromInt(accrualData.getNumberOfDaysInMonth()); switch
|
||||||
|
* (type) { case DAYS_360: numberOfDays = 360 * accrualData.getRepayEvery();
|
||||||
private int calculateTotalNumberOfDaysForYear(final int totalNumberOfDays, final LoanScheduleAccrualData accrualData) {
|
* break; case DAYS_364: numberOfDays = 364 * accrualData.getRepayEvery();
|
||||||
int numberOfDays = totalNumberOfDays;
|
* break; case DAYS_365: numberOfDays = 365 * accrualData.getRepayEvery();
|
||||||
DaysInYearType type = DaysInYearType.fromInt(accrualData.getNumberOfDaysInMonth());
|
* break; default: break; } return numberOfDays; }
|
||||||
switch (type) {
|
*/
|
||||||
case DAYS_360:
|
|
||||||
numberOfDays = 360 * accrualData.getRepayEvery();
|
|
||||||
break;
|
|
||||||
case DAYS_364:
|
|
||||||
numberOfDays = 364 * accrualData.getRepayEvery();
|
|
||||||
break;
|
|
||||||
case DAYS_365:
|
|
||||||
numberOfDays = 365 * accrualData.getRepayEvery();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return numberOfDays;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) throws Exception {
|
public void addAccrualAccounting(LoanScheduleAccrualData scheduleAccrualData) throws Exception {
|
||||||
|
|||||||
+9
-4
@@ -383,12 +383,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
|
|
||||||
final LocalDate earliestUnpaidInstallmentDate = LocalDate.now();
|
final LocalDate earliestUnpaidInstallmentDate = LocalDate.now();
|
||||||
|
|
||||||
final Money precloseAmount = loan.fetchPrepaymentDetail(this.loanScheduleFactory);
|
final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loan.fetchPrepaymentDetail(this.loanScheduleFactory);
|
||||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
||||||
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
|
final Collection<CodeValueData> paymentOptions = this.codeValueReadPlatformService
|
||||||
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
|
.retrieveCodeValuesByCode(PaymentDetailConstants.paymentTypeCodeName);
|
||||||
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
|
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
|
||||||
precloseAmount.getAmount(), null, null, null, null, null, paymentOptions, null, null, null);
|
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment
|
||||||
|
.getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency)
|
||||||
|
.getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||||
|
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, paymentOptions, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1450,8 +1453,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
final Long officeId = rs.getLong("officeId");
|
final Long officeId = rs.getLong("officeId");
|
||||||
final LocalDate accruedTill = JdbcSupport.getLocalDate(rs, "accruedTill");
|
final LocalDate accruedTill = JdbcSupport.getLocalDate(rs, "accruedTill");
|
||||||
final LocalDate interestCalculatedFrom = JdbcSupport.getLocalDate(rs, "interestCalculatedFrom");
|
final LocalDate interestCalculatedFrom = JdbcSupport.getLocalDate(rs, "interestCalculatedFrom");
|
||||||
final Integer numberOfDaysInMonth = JdbcSupport.getInteger(rs, "daysInMonth");;
|
final Integer numberOfDaysInMonth = JdbcSupport.getInteger(rs, "daysInMonth");
|
||||||
final Integer numberOfDaysInYear = JdbcSupport.getInteger(rs, "daysInYear");;
|
;
|
||||||
|
final Integer numberOfDaysInYear = JdbcSupport.getInteger(rs, "daysInYear");
|
||||||
|
;
|
||||||
final Integer frequencyEnum = JdbcSupport.getInteger(rs, "frequencyEnum");
|
final Integer frequencyEnum = JdbcSupport.getInteger(rs, "frequencyEnum");
|
||||||
final Integer repayEvery = JdbcSupport.getInteger(rs, "repayEvery");
|
final Integer repayEvery = JdbcSupport.getInteger(rs, "repayEvery");
|
||||||
final PeriodFrequencyType frequency = PeriodFrequencyType.fromInt(frequencyEnum);
|
final PeriodFrequencyType frequency = PeriodFrequencyType.fromInt(frequencyEnum);
|
||||||
|
|||||||
+4
@@ -44,4 +44,8 @@ public enum AmortizationMethod {
|
|||||||
}
|
}
|
||||||
return repaymentMethod;
|
return repaymentMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEqualInstallment() {
|
||||||
|
return this.value.equals(AmortizationMethod.EQUAL_INSTALLMENTS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-1
@@ -24,6 +24,7 @@ public class LoanRepaymentScheduleInstallmentBuilder {
|
|||||||
private final Money feeCharges = new MoneyBuilder().build();
|
private final Money feeCharges = new MoneyBuilder().build();
|
||||||
private final Money penaltyCharges = new MoneyBuilder().build();
|
private final Money penaltyCharges = new MoneyBuilder().build();
|
||||||
private boolean completed = false;
|
private boolean completed = false;
|
||||||
|
private boolean recalculatedInterestComponent = false;
|
||||||
|
|
||||||
public LoanRepaymentScheduleInstallmentBuilder(final MonetaryCurrency currencyDetail) {
|
public LoanRepaymentScheduleInstallmentBuilder(final MonetaryCurrency currencyDetail) {
|
||||||
this.currencyDetail = currencyDetail;
|
this.currencyDetail = currencyDetail;
|
||||||
@@ -34,7 +35,7 @@ public class LoanRepaymentScheduleInstallmentBuilder {
|
|||||||
public LoanRepaymentScheduleInstallment build() {
|
public LoanRepaymentScheduleInstallment build() {
|
||||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this.loan, this.installmentNumber,
|
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this.loan, this.installmentNumber,
|
||||||
this.fromDate, this.dueDate, this.principal.getAmount(), this.interest.getAmount(), this.feeCharges.getAmount(),
|
this.fromDate, this.dueDate, this.principal.getAmount(), this.interest.getAmount(), this.feeCharges.getAmount(),
|
||||||
this.penaltyCharges.getAmount());
|
this.penaltyCharges.getAmount(), this.recalculatedInterestComponent);
|
||||||
if (this.completed) {
|
if (this.completed) {
|
||||||
installment.payPrincipalComponent(this.latestTransactionDate, this.principal);
|
installment.payPrincipalComponent(this.latestTransactionDate, this.principal);
|
||||||
installment.payInterestComponent(this.latestTransactionDate, this.interest);
|
installment.payInterestComponent(this.latestTransactionDate, this.interest);
|
||||||
|
|||||||
Reference in New Issue
Block a user