Merge pull request #1066 from pramodn02/develop
MIFOSX-1517 : corrected recalculation for tranche loans
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@ CREATE TABLE `m_loan_transaction_temp` (
|
||||
INSERT INTO m_loan_transaction_temp(`id`,`loan_id`,`transaction_date`,`amount`) select lt.id, lt.loan_id,lt.transaction_date,if(lt.transaction_type_enum = 1 , IFNULL(lt.amount,0),IFNULL(-lt.principal_portion_derived,0)) from m_loan_transaction lt where lt.is_reversed=0;
|
||||
|
||||
|
||||
UPDATE m_loan_transaction lt SET lt.outstanding_loan_balance_derived = (select sum(ltt.amount) from m_loan_transaction_temp ltt where ltt.id <= lt.id and ltt.transaction_date <= lt.transaction_date and ltt.loan_id = lt.loan_id) where lt.transaction_type_enum != 10 and lt.is_reversed = 0;
|
||||
UPDATE m_loan_transaction lt SET lt.outstanding_loan_balance_derived = (select sum(ltt.amount) from m_loan_transaction_temp ltt where ((ltt.transaction_date = lt.transaction_date and ltt.id <= lt.id) or ltt.transaction_date < lt.transaction_date) and ltt.loan_id = lt.loan_id) where lt.transaction_type_enum != 10 and lt.is_reversed = 0;
|
||||
|
||||
DROP TABLE `m_loan_transaction_temp`;
|
||||
|
||||
|
||||
+2
-2
@@ -354,9 +354,9 @@ public class LoansApiResource {
|
||||
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData = loanBasicDetails.repaymentScheduleRelatedData();
|
||||
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(loanId, repaymentScheduleRelatedData,
|
||||
disbursementData);
|
||||
disbursementData, loanBasicDetails.isInterestRecalculationEnabled());
|
||||
|
||||
if (associationParameters.contains("futureSchedule")) {
|
||||
if (associationParameters.contains("futureSchedule") && loanBasicDetails.isInterestRecalculationEnabled()) {
|
||||
mandatoryResponseParameters.add("futureSchedule");
|
||||
this.calculationPlatformService.updateFutureSchedule(repaymentSchedule, loanId);
|
||||
}
|
||||
|
||||
+13
-1
@@ -1817,7 +1817,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()
|
||||
&& fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now())) {
|
||||
&& (fetchRepaymentScheduleInstallment(1).getDueDate().isBefore(LocalDate.now()) || isDisbursementMissed())) {
|
||||
LocalDate recalculateFrom = null;
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(loanScheduleFactory, currency, calculatedRepaymentsStartingFromDate,
|
||||
isHolidayEnabled, holidays, workingDays, calendarInstanceForInterestRecalculation, recalculateFrom);
|
||||
@@ -1960,6 +1960,18 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return details;
|
||||
}
|
||||
|
||||
private boolean isDisbursementMissed() {
|
||||
boolean isDisbursementMissed = false;
|
||||
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
|
||||
if (disbursementDetail.actualDisbursementDate() == null
|
||||
&& LocalDate.now().isAfter(disbursementDetail.expectedDisbursementDateAsLocalDate())) {
|
||||
isDisbursementMissed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isDisbursementMissed;
|
||||
}
|
||||
|
||||
private BigDecimal getDisbursedAmount() {
|
||||
BigDecimal principal = BigDecimal.ZERO;
|
||||
for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) {
|
||||
|
||||
+12
-7
@@ -24,6 +24,7 @@ import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
@Table(name = "m_loan_disbursement_detail")
|
||||
public class LoanDisbursementDetails extends AbstractPersistable<Long> {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "loan_id", nullable = false)
|
||||
private Loan loan;
|
||||
@@ -77,6 +78,14 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
|
||||
return this.expectedDisbursementDate;
|
||||
}
|
||||
|
||||
public LocalDate expectedDisbursementDateAsLocalDate() {
|
||||
LocalDate expectedDisburseDate = null;
|
||||
if (this.expectedDisbursementDate != null) {
|
||||
expectedDisburseDate = new LocalDate(this.expectedDisbursementDate);
|
||||
}
|
||||
return expectedDisburseDate;
|
||||
}
|
||||
|
||||
public Date actualDisbursementDate() {
|
||||
return this.actualDisbursementDate;
|
||||
}
|
||||
@@ -102,14 +111,10 @@ public class LoanDisbursementDetails extends AbstractPersistable<Long> {
|
||||
}
|
||||
|
||||
public DisbursementData toData() {
|
||||
LocalDate expectedDisburseDate = null;
|
||||
LocalDate expectedDisburseDate = expectedDisbursementDateAsLocalDate();
|
||||
LocalDate actualDisburseDate = null;
|
||||
if (this.expectedDisbursementDate != null) {
|
||||
expectedDisburseDate = new LocalDate(this.expectedDisbursementDate);
|
||||
actualDisburseDate = expectedDisburseDate;
|
||||
}
|
||||
if (getDisbursementDate() != null) {
|
||||
actualDisburseDate = new LocalDate(getDisbursementDate());
|
||||
if (this.actualDisbursementDate != null) {
|
||||
actualDisburseDate = new LocalDate(this.actualDisbursementDate);
|
||||
}
|
||||
return new DisbursementData(getId(), expectedDisburseDate, actualDisburseDate, this.principal, this.approvedPrincipal);
|
||||
}
|
||||
|
||||
+172
-67
@@ -158,20 +158,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
final int daysInPeriod = Days.daysBetween(periodStartDate, scheduledDueDate).getDays();
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
||||
BigDecimal disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, scheduledDueDate, periods,
|
||||
BigDecimal.ZERO);
|
||||
principalDisbursed = principalDisbursed.plus(disburseAmt);
|
||||
loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt));
|
||||
outstandingBalance = outstandingBalance.plus(disburseAmt);
|
||||
if (loanApplicationTerms.getMaxOutstandingBalance() != null
|
||||
&& outstandingBalance.isGreaterThan(loanApplicationTerms.getMaxOutstandingBalance())) {
|
||||
String errorMsg = "Outstanding balance must not exceed the amount: " + loanApplicationTerms.getMaxOutstandingBalance();
|
||||
throw new MultiDisbursementOutstandingAmoutException(errorMsg, loanApplicationTerms.getMaxOutstandingBalance()
|
||||
.getAmount(), disburseAmt);
|
||||
}
|
||||
}
|
||||
int daysInPeriodApplicableForInterest = daysInPeriod;
|
||||
|
||||
if (periodStartDate.isBefore(idealDisbursementDate)) {
|
||||
@@ -203,8 +189,37 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest,
|
||||
scheduledDueDateAsPerFrequency, loanApplicationTerms.getInterestChargedFromLocalDate(),
|
||||
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
|
||||
BigDecimal interestToBeAdded = BigDecimal.ZERO;
|
||||
BigDecimal disburseAmt = BigDecimal.ZERO;
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
loanApplicationTerms.setFixedEmiAmountForPeriod(scheduledDueDate);
|
||||
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
||||
disburseAmt = disbursementForPeriod(loanApplicationTerms, periodStartDate, scheduledDueDate, disbursementDatas,
|
||||
diffAmt != null);
|
||||
principalDisbursed = principalDisbursed.plus(disburseAmt);
|
||||
loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseAmt));
|
||||
outstandingBalance = outstandingBalance.plus(disburseAmt);
|
||||
if (loanApplicationTerms.getMaxOutstandingBalance() != null
|
||||
&& outstandingBalance.isGreaterThan(loanApplicationTerms.getMaxOutstandingBalance())) {
|
||||
String errorMsg = "Outstanding balance must not exceed the amount: " + loanApplicationTerms.getMaxOutstandingBalance();
|
||||
throw new MultiDisbursementOutstandingAmoutException(errorMsg, loanApplicationTerms.getMaxOutstandingBalance()
|
||||
.getAmount(), disburseAmt);
|
||||
}
|
||||
for (DisbursementData disbursementData : disbursementDatas) {
|
||||
Money disbursedAmt = Money.of(currency, disbursementData.amount());
|
||||
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
|
||||
disbursementData.disbursementDate(), disbursedAmt, chargesDueAtTimeOfDisbursement);
|
||||
periods.add(disbursementPeriod);
|
||||
if (disbursementData.disbursementDate().isAfter(periodStartDateApplicableForInterest)) {
|
||||
interestToBeAdded = interestToBeAdded.add(calculateInterestForSpecificDays(mc, loanApplicationTerms, periodNumber,
|
||||
totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableInFullInstallment,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, disbursementData.disbursementDate(),
|
||||
scheduledDueDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Money balanceForcalculation = outstandingBalance;
|
||||
Money balanceForcalculation = outstandingBalance.minus(disburseAmt);
|
||||
// reduce principal is the early payment, will processed as per the
|
||||
// reschedule strategy
|
||||
if (reducePrincipal.isGreaterThanZero()) {
|
||||
@@ -249,53 +264,36 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
// update cumulative fields for principal & interest
|
||||
Money interestForThisinstallment = principalInterestForThisPeriod.interest();
|
||||
|
||||
if (daysCalcForInstallmentNumber == periodNumber) {
|
||||
interestForThisinstallment = interestForThisinstallment.zero().plus(
|
||||
calculateInterestForDays(daysInPeriodApplicableInFullInstallment, interestForThisinstallment.getAmount(),
|
||||
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);
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
|
||||
Money actualOutstandingbalance = outstandingBalance;
|
||||
if (interestForThisinstallment.isGreaterThanZero()) {
|
||||
interestForThisinstallment = interestForThisinstallment.plus(interestToBeAdded);
|
||||
} else if (principalInterestForThisPeriod.interestPaymentDueToGrace().isGreaterThanZero()) {
|
||||
totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace.minus(interestToBeAdded);
|
||||
}
|
||||
|
||||
Money principalForThisPeriod = principalDisbursed.zero();
|
||||
Money outstandingForThePeriod = outstandingBalance;
|
||||
Money reducePrincipalForCurrentInstallment = reducePrincipal;
|
||||
|
||||
totalCumulativeInterest = totalCumulativeInterest.plus(interestForThisinstallment);
|
||||
|
||||
Money feeChargesForInstallment = principalDisbursed.zero();
|
||||
Money penaltyChargesForInstallment = principalDisbursed.zero();
|
||||
|
||||
if (principalForThisPeriod.isGreaterThan(reducePrincipal)) {
|
||||
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipal);
|
||||
reducePrincipal = reducePrincipal.zero();
|
||||
} else {
|
||||
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
|
||||
principalForThisPeriod = principalForThisPeriod.zero();
|
||||
}
|
||||
|
||||
if (periodNumber == 1) {
|
||||
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
|
||||
}
|
||||
|
||||
Money extraPrincipal = reducePrincipal.zero();
|
||||
Money canBeReducedFromReducePrincipal = reducePrincipal.zero();
|
||||
// this block is to identify interest based on late/early payment
|
||||
if (diffAmt != null && !diffAmt.isEmpty()) {
|
||||
BigDecimal interestDueToLatePayment = BigDecimal.ZERO;
|
||||
BigDecimal interestReducedDueToEarlyPayment = BigDecimal.ZERO;
|
||||
|
||||
boolean isperiodPrincipalReduced = false;
|
||||
for (RecalculationDetail detail : diffAmt) {
|
||||
if (!detail.isInterestompound()) {
|
||||
// will increase the principal portion and reduces
|
||||
@@ -307,9 +305,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
reducePrincipal = reducePrincipal.plus(detail.getAmount());
|
||||
extraPrincipal = extraPrincipal.plus(detail.getAmount());
|
||||
int diffDays = Days.daysBetween(detail.getStartDate(), scheduledDueDate).getDays();
|
||||
if (diffDays > 0 && !isperiodPrincipalReduced) {
|
||||
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
|
||||
isperiodPrincipalReduced = true;
|
||||
if (diffDays > 0) {
|
||||
canBeReducedFromReducePrincipal = canBeReducedFromReducePrincipal.plus(detail.getAmount());
|
||||
}
|
||||
|
||||
LocalDate startDate = detail.getStartDate();
|
||||
@@ -363,16 +360,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
}
|
||||
|
||||
if (reducePrincipal.isLessThanZero()) {
|
||||
reducePrincipal = reducePrincipal.zero();
|
||||
} else {
|
||||
Money actualOutstanding = outstandingBalance.minus(reducePrincipal);
|
||||
principalForThisPeriod = reducePrincipal.plus(principalForThisPeriod);
|
||||
if (actualOutstanding.isLessThanZero()) {
|
||||
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
|
||||
}
|
||||
}
|
||||
|
||||
if (totalOutstandingInterestPaymentDueToGrace.isGreaterThanZero()) {
|
||||
totalOutstandingInterestPaymentDueToGrace = totalOutstandingInterestPaymentDueToGrace.plus(interestDueToLatePayment)
|
||||
.minus(interestReducedDueToEarlyPayment);
|
||||
@@ -384,7 +371,60 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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);
|
||||
principalForThisPeriod = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisPeriod,
|
||||
totalCumulativePrincipal.plus(principalForThisPeriod), periodNumber);
|
||||
}
|
||||
totalCumulativePrincipal = totalCumulativePrincipal.plus(principalForThisPeriod);
|
||||
|
||||
// 6. update outstandingLoanBlance using correct 'principalDue'
|
||||
outstandingBalance = outstandingBalance.minus(principalForThisPeriod);
|
||||
if (outstandingBalance.isLessThanZero()) {
|
||||
principalForThisPeriod = principalForThisPeriod.plus(outstandingBalance);
|
||||
outstandingBalance = outstandingBalance.zero();
|
||||
}
|
||||
}
|
||||
Money actualOutstandingbalance = outstandingBalance;
|
||||
if (diffAmt != null && !diffAmt.isEmpty()) {
|
||||
reducePrincipal = reducePrincipal.minus(canBeReducedFromReducePrincipal);
|
||||
Money pricipalAfterCurrentPrincipal = canBeReducedFromReducePrincipal.minus(principalForThisPeriod);
|
||||
if (pricipalAfterCurrentPrincipal.isGreaterThanZero()) {
|
||||
reducePrincipal = reducePrincipal.plus(pricipalAfterCurrentPrincipal);
|
||||
}
|
||||
|
||||
if (reducePrincipal.isLessThanZero()) {
|
||||
reducePrincipal = reducePrincipal.zero();
|
||||
} else {
|
||||
Money actualOutstanding = outstandingBalance.minus(reducePrincipal);
|
||||
principalForThisPeriod = reducePrincipal.plus(principalForThisPeriod);
|
||||
if (actualOutstanding.isLessThanZero()) {
|
||||
principalForThisPeriod = principalForThisPeriod.plus(actualOutstanding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (principalForThisPeriod.isGreaterThan(reducePrincipalForCurrentInstallment)) {
|
||||
principalForThisPeriod = principalForThisPeriod.minus(reducePrincipalForCurrentInstallment);
|
||||
reducePrincipal = reducePrincipal.minus(reducePrincipalForCurrentInstallment);
|
||||
} else {
|
||||
reducePrincipal = reducePrincipal.minus(principalForThisPeriod);
|
||||
principalForThisPeriod = principalForThisPeriod.zero();
|
||||
}
|
||||
|
||||
if (periodNumber == 1) {
|
||||
fixedEmiAmount = principalForThisPeriod.plus(interestForThisinstallment);
|
||||
}
|
||||
|
||||
actualOutstandingbalance = actualOutstandingbalance.minus(reducePrincipal);
|
||||
if (actualOutstandingbalance.isLessThanZero()) {
|
||||
actualOutstandingbalance = actualOutstandingbalance.zero();
|
||||
}
|
||||
|
||||
// 8. sum up real totalInstallmentDue from components
|
||||
final Money totalInstallmentDue = principalForThisPeriod//
|
||||
@@ -415,7 +455,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
instalmentNumber++;
|
||||
}
|
||||
|
||||
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse)) {
|
||||
if (principalDisbursed.isNotEqualTo(expectedPrincipalDisburse) && diffAmt == null) {
|
||||
final String errorMsg = "One of the Disbursement date is not falling on Loan Schedule";
|
||||
throw new MultiDisbursementDisbursementDateException(errorMsg);
|
||||
}
|
||||
@@ -870,17 +910,31 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
|
||||
private BigDecimal disbursementForPeriod(final LoanApplicationTerms loanApplicationTerms, LocalDate startDate, LocalDate endDate,
|
||||
final Collection<LoanScheduleModelPeriod> periods, final BigDecimal chargesDueAtTimeOfDisbursement) {
|
||||
final Collection<DisbursementData> disbursementDatas, boolean excludePastUndisbursed) {
|
||||
BigDecimal principal = BigDecimal.ZERO;
|
||||
MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
|
||||
for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) {
|
||||
if (disbursementData.isDueForDisbursement(startDate, endDate)) {
|
||||
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement(
|
||||
disbursementData.disbursementDate(), Money.of(currency, disbursementData.amount()), chargesDueAtTimeOfDisbursement);
|
||||
periods.add(disbursementPeriod);
|
||||
if (!excludePastUndisbursed
|
||||
|| (excludePastUndisbursed && (disbursementData.isDisbursed() || !disbursementData.disbursementDate().isBefore(
|
||||
LocalDate.now())))) {
|
||||
if (disbursementData.isDueForDisbursement(startDate, endDate)) {
|
||||
disbursementDatas.add(disbursementData);
|
||||
principal = principal.add(disbursementData.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
|
||||
private BigDecimal disbursementAfterPeriod(final LoanApplicationTerms loanApplicationTerms, LocalDate endDate,
|
||||
LocalDate lastinstallmentDueDate) {
|
||||
BigDecimal principal = BigDecimal.ZERO;
|
||||
for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) {
|
||||
|
||||
if (disbursementData.disbursementDate().isAfter(endDate) && lastinstallmentDueDate.isAfter(disbursementData.disbursementDate())) {
|
||||
principal = principal.add(disbursementData.amount());
|
||||
}
|
||||
}
|
||||
|
||||
return principal;
|
||||
}
|
||||
|
||||
@@ -1413,6 +1467,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Money inerestForCurrentInstallment = Money.zero(currency);
|
||||
Money principalOutstanding = Money.zero(currency);
|
||||
Integer periodNumber = 0;
|
||||
LocalDate lastDueDate = loanApplicationTerms.getExpectedDisbursementDate();
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
|
||||
if (currentInstallment.isNotFullyPaidOff()) {
|
||||
if (!currentInstallment.getDueDate().isAfter(onDate)) {
|
||||
@@ -1443,6 +1498,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
}
|
||||
}
|
||||
if (!currentInstallment.isRecalculatedInterestComponent() && currentInstallment.getDueDate().isAfter(lastDueDate)) {
|
||||
lastDueDate = currentInstallment.getDueDate();
|
||||
}
|
||||
}
|
||||
if (interestChargedFromLocalDate != null && calculateInterestFrom.isBefore(interestChargedFromLocalDate)) {
|
||||
calculateInterestFrom = interestChargedFromLocalDate;
|
||||
@@ -1463,6 +1521,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
if (useDailyInterest) {
|
||||
interestForInstallment = loanApplicationTerms.interestRateFor(this.paymentPeriodsInOneYearCalculator, mc,
|
||||
principalOutstanding, calculateInterestFrom, repaymentDueDate);
|
||||
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment));
|
||||
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days));
|
||||
|
||||
} else {
|
||||
LocalDate firstRepaymentdate = this.scheduledDateGenerator.generateNextRepaymentDate(
|
||||
@@ -1485,13 +1545,58 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
loanApplicationTerms.getInterestChargedFromLocalDate(),
|
||||
loanApplicationTerms.getLoanTermPeriodFrequencyType(), loanApplicationTerms.getRepaymentEvery());
|
||||
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
BigDecimal reducePrincipal = disbursementAfterPeriod(loanApplicationTerms, repaymentDueDate, lastDueDate);
|
||||
principalOutstanding = principalOutstanding.minus(reducePrincipal);
|
||||
}
|
||||
|
||||
PrincipalInterest principalInterest = loanApplicationTerms.calculateTotalInterestForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction, periodNumber, mc,
|
||||
totalOutstandingInterestPaymentDueToGrace.zero(), daysInPeriodApplicableForInterest, principalOutstanding);
|
||||
interestForInstallment = principalInterest.interest().getAmount();
|
||||
Money alteredPrincipal = principalOutstanding;
|
||||
Money reducePricipal = principalOutstanding.zero();
|
||||
BigDecimal interestToBeReduced = BigDecimal.ZERO;
|
||||
BigDecimal interestForExtraPrincipal = BigDecimal.ZERO;
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
final Collection<DisbursementData> disbursementDatas = new ArrayList<>();
|
||||
BigDecimal addedPrincipal = disbursementForPeriod(loanApplicationTerms, calculateInterestFrom, repaymentDueDate,
|
||||
disbursementDatas, true);
|
||||
alteredPrincipal = alteredPrincipal.minus(addedPrincipal);
|
||||
for (DisbursementData disbursementData : disbursementDatas) {
|
||||
Money disbursedAmt = Money.of(currency, disbursementData.amount());
|
||||
if (disbursementData.disbursementDate().isAfter(calculateInterestFrom)) {
|
||||
LocalDate calculateTill = disbursementData.disbursementDate();
|
||||
interestToBeReduced = interestToBeReduced.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
|
||||
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt, calculateInterestFrom, calculateTill));
|
||||
|
||||
if (onDate.isAfter(calculateTill)) {
|
||||
interestForExtraPrincipal = interestForExtraPrincipal.add(calculateInterestForSpecificDays(mc,
|
||||
loanApplicationTerms, periodNumber, totalOutstandingInterestPaymentDueToGrace,
|
||||
daysInPeriodApplicableForInterest, interestCalculationGraceOnRepaymentPeriodFraction, disbursedAmt,
|
||||
calculateTill, onDate));
|
||||
} else {
|
||||
reducePricipal = reducePricipal.plus(disbursedAmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment).plus(interestToBeReduced));
|
||||
BigDecimal interestForCloseDate = BigDecimal.ZERO;
|
||||
if (alteredPrincipal.isNotEqualTo(principalOutstanding)) {
|
||||
interestForCloseDate = interestForExtraPrincipal;
|
||||
interestForCloseDate = interestForCloseDate.add(calculateInterestForSpecificDays(mc, loanApplicationTerms,
|
||||
periodNumber, totalOutstandingInterestPaymentDueToGrace, daysInPeriodApplicableForInterest,
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, alteredPrincipal, calculateInterestFrom, onDate));
|
||||
} else {
|
||||
interestForCloseDate = interestForCloseDate.add(BigDecimal.valueOf(calculateInterestForDays(
|
||||
daysInPeriodApplicableForInterest, interestForInstallment, days)));
|
||||
}
|
||||
interest = interest.plus(interestForCloseDate);
|
||||
principalOutstanding = principalOutstanding.minus(reducePricipal);
|
||||
}
|
||||
|
||||
}
|
||||
interest = interest.plus(inerestForCurrentInstallment.minus(interestForInstallment));
|
||||
interest = interest.plus(calculateInterestForDays(daysInPeriodApplicableForInterest, interestForInstallment, days));
|
||||
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -397,7 +397,7 @@ public class LoanScheduleAssembler {
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
|
||||
CalendarInstance calendarInstance = loanApplicationTerms.getRestCalendarInstance();
|
||||
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate);
|
||||
LocalDate nextScheduleDate = CalendarUtils.getNextScheduleDate(calendarInstance.getCalendar(), onDate.minusDays(1));
|
||||
if (loanApplicationTerms.getRecalculationFrequencyType().isSameAsRepayment()) {
|
||||
nextScheduleDate = scheduledDateGenerator.adjustRepaymentDate(nextScheduleDate, loanApplicationTerms, isHolidayEnabled,
|
||||
holidays, workingDays);
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ public interface LoanReadPlatformService {
|
||||
LoanAccountData retrieveOne(Long loanId);
|
||||
|
||||
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData,
|
||||
Collection<DisbursementData> disbursementData);
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled);
|
||||
|
||||
Collection<LoanTransactionData> retrieveLoanTransactions(Long loanId);
|
||||
|
||||
|
||||
+26
-13
@@ -177,13 +177,14 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
@Override
|
||||
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData) {
|
||||
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
|
||||
boolean isInterestRecalculationEnabled) {
|
||||
|
||||
try {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
|
||||
repaymentScheduleRelatedLoanData, disbursementData);
|
||||
repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled);
|
||||
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
|
||||
|
||||
return this.jdbcTemplate.query(sql, fullResultsetExtractor, new Object[] { loanId });
|
||||
@@ -862,15 +863,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private final Collection<DisbursementData> disbursementData;
|
||||
private LocalDate lastDueDate;
|
||||
private BigDecimal outstandingLoanPrincipalBalance;
|
||||
private boolean excludePastUndisbursed;
|
||||
|
||||
public LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
|
||||
Collection<DisbursementData> disbursementData) {
|
||||
Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled) {
|
||||
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
|
||||
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
|
||||
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
|
||||
this.lastDueDate = this.disbursement.disbursementDate();
|
||||
this.outstandingLoanPrincipalBalance = this.disbursement.amount();
|
||||
this.disbursementData = disbursementData;
|
||||
this.excludePastUndisbursed = isInterestRecalculationEnabled;
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
@@ -899,6 +902,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
periods.add(disbursementPeriod);
|
||||
totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.amount()).getAmount();
|
||||
} else {
|
||||
if (!this.disbursement.isDisbursed()) {
|
||||
excludePastUndisbursed = false;
|
||||
}
|
||||
this.outstandingLoanPrincipalBalance = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
@@ -930,7 +936,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final LocalDate dueDate = JdbcSupport.getLocalDate(rs, "dueDate");
|
||||
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
|
||||
final boolean complete = rs.getBoolean("complete");
|
||||
|
||||
if (disbursementData != null) {
|
||||
BigDecimal principal = BigDecimal.ZERO;
|
||||
for (DisbursementData data : disbursementData) {
|
||||
@@ -939,14 +944,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), this.totalFeeChargesDueAtDisbursement, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
} else if (data.isDueForDisbursement(fromDate, dueDate)) {
|
||||
principal = principal.add(data.amount());
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
} else if (data.isDueForDisbursement(fromDate, dueDate)
|
||||
&& this.outstandingLoanPrincipalBalance.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (!excludePastUndisbursed
|
||||
|| (excludePastUndisbursed && (data.isDisbursed() || !data.disbursementDate().isBefore(LocalDate.now())))) {
|
||||
principal = principal.add(data.amount());
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
data.disbursementDate(), data.amount(), BigDecimal.ZERO, data.isDisbursed());
|
||||
periods.add(periodData);
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
}
|
||||
}
|
||||
}
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(principal);
|
||||
totalPrincipalDisbursed = totalPrincipalDisbursed.add(principal);
|
||||
}
|
||||
|
||||
@@ -1588,15 +1598,18 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
sqlBuilder.append("SELECT ml.id FROM m_loan ml ");
|
||||
sqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id ");
|
||||
sqlBuilder.append(" LEFT JOIN m_loan_disbursement_detail dd on dd.loan_id=ml.id and dd.disbursedon_date is null ");
|
||||
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
|
||||
sqlBuilder.append(" and ml.interest_recalculation_enabled = 1 ");
|
||||
sqlBuilder.append(" and ml.is_npa = 0 ");
|
||||
sqlBuilder.append(" and mr.completed_derived is false ");
|
||||
sqlBuilder.append(" and mr.duedate < ? ");
|
||||
sqlBuilder.append(" and ((");
|
||||
sqlBuilder.append(" mr.completed_derived is false ");
|
||||
sqlBuilder.append(" and mr.duedate < ? )");
|
||||
sqlBuilder.append(" or dd.expected_disburse_date < ? ) ");
|
||||
sqlBuilder.append(" group by ml.id");
|
||||
try {
|
||||
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class,
|
||||
new Object[] { LoanStatus.ACTIVE.getValue(), formatter.print(LocalDate.now()) });
|
||||
new Object[] { LoanStatus.ACTIVE.getValue(), formatter.print(LocalDate.now()),formatter.print(LocalDate.now()) });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,6 +46,6 @@ public enum AmortizationMethod {
|
||||
}
|
||||
|
||||
public boolean isEqualInstallment() {
|
||||
return this.value.equals(AmortizationMethod.EQUAL_INSTALLMENTS);
|
||||
return this.value.equals(AmortizationMethod.EQUAL_INSTALLMENTS.getValue());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user