[FINERACT-1958] Repayment schedule for down payment
This commit is contained in:
+15
-13
@@ -1264,13 +1264,14 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
this.repaymentScheduleInstallments.clear();
|
||||
for (final LoanScheduleModelPeriod scheduledLoanInstallment : modifiedLoanSchedule.getPeriods()) {
|
||||
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod()) {
|
||||
if (scheduledLoanInstallment.isRepaymentPeriod() || scheduledLoanInstallment.isDownPaymentPeriod()) {
|
||||
final LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(this,
|
||||
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
|
||||
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
|
||||
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
|
||||
scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(),
|
||||
scheduledLoanInstallment.getLoanCompoundingDetails());
|
||||
scheduledLoanInstallment.getLoanCompoundingDetails(), scheduledLoanInstallment.rescheduleInterestPortion(),
|
||||
scheduledLoanInstallment.isDownPaymentPeriod());
|
||||
addLoanRepaymentScheduleInstallment(installment);
|
||||
}
|
||||
}
|
||||
@@ -2477,7 +2478,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) {
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail, boolean downPaymentEnabled) {
|
||||
|
||||
final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
|
||||
|
||||
@@ -2495,7 +2496,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
validateDisbursementDateIsOnHoliday(holidayDetailDTO.isAllowTransactionsOnHoliday(), holidayDetailDTO.getHolidays());
|
||||
|
||||
regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(this.repaymentScheduleDetail().isInterestRecalculationEnabled(),
|
||||
isDisbursementMissed(), scheduleGeneratorDTO);
|
||||
isDisbursementMissed(), scheduleGeneratorDTO, downPaymentEnabled);
|
||||
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
|
||||
@@ -2527,14 +2528,16 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
private void regenerateRepaymentScheduleWithInterestRecalculationIfNeeded(boolean interestRecalculationEnabledParam,
|
||||
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO) {
|
||||
boolean disbursementMissedParam, ScheduleGeneratorDTO scheduleGeneratorDTO, final boolean downPaymentEnabled) {
|
||||
|
||||
LocalDate firstInstallmentDueDate = fetchRepaymentScheduleInstallment(1).getDueDate();
|
||||
if (interestRecalculationEnabledParam
|
||||
&& (firstInstallmentDueDate.isBefore(DateUtils.getBusinessLocalDate()) || disbursementMissedParam)) {
|
||||
if ((interestRecalculationEnabledParam
|
||||
&& (firstInstallmentDueDate.isBefore(DateUtils.getBusinessLocalDate()) || disbursementMissedParam))) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO);
|
||||
} else if (downPaymentEnabled) {
|
||||
LoanScheduleDTO loanSchedule = getRecalculatedSchedule(scheduleGeneratorDTO);
|
||||
updateLoanSchedule(loanSchedule.getInstallments());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<LoanDisbursementDetails> getDisbursedLoanDisbursementDetails() {
|
||||
@@ -2965,7 +2968,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
externalId = ExternalId.generate();
|
||||
}
|
||||
Money downPaymentMoney = Money.of(getCurrency(),
|
||||
MathUtil.percentageOf(disbursedAmount, disbursedAmountPercentageForDownPayment, 9));
|
||||
MathUtil.percentageOf(disbursedAmount, disbursedAmountPercentageForDownPayment, 19));
|
||||
LoanTransaction downPaymentTransaction = LoanTransaction.downPayment(getOffice(), downPaymentMoney, null, disbursedOn,
|
||||
externalId);
|
||||
/*
|
||||
@@ -5553,7 +5556,6 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
public void regenerateRepaymentScheduleWithInterestRecalculation(final ScheduleGeneratorDTO generatorDTO) {
|
||||
|
||||
LocalDate lastTransactionDate = getLastUserTransactionDate();
|
||||
final LoanScheduleDTO loanSchedule = getRecalculatedSchedule(generatorDTO);
|
||||
if (loanSchedule == null) {
|
||||
@@ -5788,15 +5790,15 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
}
|
||||
|
||||
private LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO) {
|
||||
|
||||
if (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || isChargedOff()) {
|
||||
if (!this.repaymentScheduleDetail().isEnableDownPayment()
|
||||
&& (!this.repaymentScheduleDetail().isInterestRecalculationEnabled() || isNpa || isChargedOff())) {
|
||||
return null;
|
||||
}
|
||||
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
|
||||
final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory().create(interestMethod);
|
||||
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mc = new MathContext(8, roundingMode);
|
||||
final MathContext mc = new MathContext(19, roundingMode);
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO);
|
||||
|
||||
|
||||
+13
@@ -131,6 +131,9 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
@Column(name = "credits_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal credits;
|
||||
|
||||
@Column(name = "is_down_payment", nullable = false)
|
||||
private boolean isDownPayment;
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "loanRepaymentScheduleInstallment")
|
||||
private Set<LoanInterestRecalcualtionAdditionalDetails> loanCompoundingDetails = new HashSet<>();
|
||||
|
||||
@@ -154,6 +157,15 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
|
||||
final BigDecimal penaltyCharges, final boolean recalculatedInterestComponent,
|
||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails, final BigDecimal rescheduleInterestPortion) {
|
||||
this(loan, installmentNumber, fromDate, dueDate, principal, interest, feeCharges, penaltyCharges, recalculatedInterestComponent,
|
||||
compoundingDetails, rescheduleInterestPortion, false);
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
|
||||
final LocalDate dueDate, final BigDecimal principal, final BigDecimal interest, final BigDecimal feeCharges,
|
||||
final BigDecimal penaltyCharges, final boolean recalculatedInterestComponent,
|
||||
final Set<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails, final BigDecimal rescheduleInterestPortion,
|
||||
final boolean isDownPayment) {
|
||||
this.loan = loan;
|
||||
this.installmentNumber = installmentNumber;
|
||||
this.fromDate = fromDate;
|
||||
@@ -169,6 +181,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
|
||||
}
|
||||
this.loanCompoundingDetails = compoundingDetails;
|
||||
this.rescheduleInterestPortion = rescheduleInterestPortion;
|
||||
this.isDownPayment = isDownPayment;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment(final Loan loan, final Integer installmentNumber, final LocalDate fromDate,
|
||||
|
||||
+60
@@ -91,6 +91,11 @@ public final class LoanSchedulePeriodData {
|
||||
totalInstallmentAmountForPeriod);
|
||||
}
|
||||
|
||||
public static LoanSchedulePeriodData downPaymentOnlyPeriod(final Integer periodNumber, final LocalDate periodDate,
|
||||
final BigDecimal principalDue, final BigDecimal principalOutstanding) {
|
||||
return new LoanSchedulePeriodData(periodNumber, periodDate, periodDate, principalDue, principalOutstanding);
|
||||
}
|
||||
|
||||
public static LoanSchedulePeriodData repaymentPeriodWithPayments(@SuppressWarnings("unused") final Long loanId,
|
||||
final Integer periodNumber, final LocalDate fromDate, final LocalDate dueDate, final LocalDate obligationsMetOnDate,
|
||||
final boolean complete, final BigDecimal principalOriginalDue, final BigDecimal principalPaid,
|
||||
@@ -265,6 +270,61 @@ public final class LoanSchedulePeriodData {
|
||||
this.totalCredits = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
// TODO refactor the class to builder pattern
|
||||
private LoanSchedulePeriodData(Integer periodNumber, LocalDate fromDate, LocalDate dueDate, BigDecimal principalDue,
|
||||
BigDecimal principalOutstanding) {
|
||||
this.period = periodNumber;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
this.obligationsMetOnDate = null;
|
||||
this.complete = null;
|
||||
this.daysInPeriod = 1;
|
||||
this.principalDisbursed = null;
|
||||
this.principalOriginalDue = principalDue;
|
||||
this.principalDue = principalOriginalDue;
|
||||
this.principalPaid = null;
|
||||
this.principalWrittenOff = null;
|
||||
this.principalOutstanding = principalOriginalDue;
|
||||
this.principalLoanBalanceOutstanding = principalOutstanding;
|
||||
|
||||
this.interestOriginalDue = null;
|
||||
this.interestDue = null;
|
||||
this.interestPaid = null;
|
||||
this.interestWaived = null;
|
||||
this.interestWrittenOff = null;
|
||||
this.interestOutstanding = null;
|
||||
|
||||
this.feeChargesDue = null;
|
||||
this.feeChargesPaid = null;
|
||||
this.feeChargesWaived = null;
|
||||
this.feeChargesWrittenOff = null;
|
||||
this.feeChargesOutstanding = null;
|
||||
|
||||
this.penaltyChargesDue = null;
|
||||
this.penaltyChargesPaid = null;
|
||||
this.penaltyChargesWaived = null;
|
||||
this.penaltyChargesWrittenOff = null;
|
||||
this.penaltyChargesOutstanding = null;
|
||||
|
||||
this.totalOriginalDueForPeriod = principalDue;
|
||||
this.totalDueForPeriod = principalDue;
|
||||
this.totalPaidForPeriod = BigDecimal.ZERO;
|
||||
this.totalPaidInAdvanceForPeriod = null;
|
||||
this.totalPaidLateForPeriod = null;
|
||||
this.totalWaivedForPeriod = null;
|
||||
this.totalWrittenOffForPeriod = null;
|
||||
this.totalOutstandingForPeriod = totalDueForPeriod;
|
||||
this.totalActualCostOfLoanForPeriod = null;
|
||||
this.totalInstallmentAmountForPeriod = totalDueForPeriod;
|
||||
|
||||
if (dueDate.isBefore(DateUtils.getBusinessLocalDate())) {
|
||||
this.totalOverdue = this.totalOutstandingForPeriod;
|
||||
} else {
|
||||
this.totalOverdue = null;
|
||||
}
|
||||
this.totalCredits = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used for creating loan schedule periods with full information on expected principal, interest & charges along
|
||||
* with what portion of each is paid.
|
||||
|
||||
+8
@@ -1737,4 +1737,12 @@ public final class LoanApplicationTerms {
|
||||
public void setNewScheduledDueDateStart(LocalDate newScheduledDueDateStart) {
|
||||
this.newScheduledDueDateStart = newScheduledDueDateStart;
|
||||
}
|
||||
|
||||
public boolean isDownPaymentEnabled() {
|
||||
return isDownPaymentEnabled;
|
||||
}
|
||||
|
||||
public BigDecimal getDisbursedAmountPercentageForDownPayment() {
|
||||
return disbursedAmountPercentageForDownPayment;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -31,6 +31,8 @@ public interface LoanScheduleModelPeriod {
|
||||
|
||||
boolean isRepaymentPeriod();
|
||||
|
||||
boolean isDownPaymentPeriod();
|
||||
|
||||
Integer periodNumber();
|
||||
|
||||
LocalDate periodFromDate();
|
||||
|
||||
Reference in New Issue
Block a user