FINERACT-1958: Fix down-payment for loan reschedule and for interest bearing loans

This commit is contained in:
Adam Saghy
2023-12-08 10:10:02 +01:00
parent 5b8f15ca95
commit ae77d29be2
20 changed files with 717 additions and 221 deletions
@@ -1082,7 +1082,7 @@ final class LoansApiResourceSwagger {
public Integer repaymentEvery;
public GetLoansLoanIdRepaymentFrequencyType repaymentFrequencyType;
@Schema(example = "24")
public Integer interestRatePerPeriod;
public BigDecimal interestRatePerPeriod;
public GetLoansLoanIdInterestRateFrequencyType interestRateFrequencyType;
@Schema(example = "24")
public Integer annualInterestRate;
@@ -1180,7 +1180,7 @@ final class LoansApiResourceSwagger {
@Schema(example = "2")
public Integer repaymentFrequencyType;
@Schema(example = "2")
public Integer interestRatePerPeriod;
public BigDecimal interestRatePerPeriod;
@Schema(example = "1")
public Integer amortizationType;
@Schema(example = "5.5")
@@ -1318,7 +1318,7 @@ final class LoansApiResourceSwagger {
@Schema(example = "0")
public Integer repaymentFrequencyType;
@Schema(example = "2")
public Integer interestRatePerPeriod;
public BigDecimal interestRatePerPeriod;
@Schema(example = "0")
public Integer interestType;
@Schema(example = "0")
@@ -180,7 +180,7 @@ public final class LoanScheduleParams {
final Money principalToBeScheduled = null;
final Money outstandingBalance = null;
final Money outstandingBalanceAsPerRest = null;
final List<LoanRepaymentScheduleInstallment> installments = null;
final List<LoanRepaymentScheduleInstallment> installments = new ArrayList<>();
final boolean partialUpdate = false;
final int loanTermInDays = 0;
final Money totalOutstandingInterestPaymentDueToGrace = null;
@@ -266,6 +266,10 @@ public final class LoanScheduleParams {
return this.periodNumber;
}
public void setPeriodNumber(int periodNumber) {
this.periodNumber = periodNumber;
}
public int getInstalmentNumber() {
return this.instalmentNumber;
}
@@ -274,12 +278,20 @@ public final class LoanScheduleParams {
return this.actualRepaymentDate;
}
public void setActualRepaymentDate(LocalDate actualRepaymentDate) {
this.actualRepaymentDate = actualRepaymentDate;
}
public Money getTotalCumulativePrincipal() {
return this.totalCumulativePrincipal;
}
public void addTotalCumulativePrincipal(final Money totalCumulativePrincipal) {
this.totalCumulativePrincipal = this.totalCumulativePrincipal.plus(totalCumulativePrincipal);
if (this.totalCumulativePrincipal != null) {
this.totalCumulativePrincipal = this.totalCumulativePrincipal.plus(totalCumulativePrincipal);
} else {
this.totalCumulativePrincipal = totalCumulativePrincipal;
}
}
public Money getTotalCumulativeInterest() {
@@ -287,7 +299,11 @@ public final class LoanScheduleParams {
}
public void addTotalCumulativeInterest(final Money totalCumulativeInterest) {
this.totalCumulativeInterest = this.totalCumulativeInterest.plus(totalCumulativeInterest);
if (this.totalCumulativeInterest != null) {
this.totalCumulativeInterest = this.totalCumulativeInterest.plus(totalCumulativeInterest);
} else {
this.totalCumulativeInterest = totalCumulativeInterest;
}
}
public Money getTotalFeeChargesCharged() {
@@ -295,7 +311,11 @@ public final class LoanScheduleParams {
}
public void addTotalFeeChargesCharged(final Money totalFeeChargesCharged) {
this.totalFeeChargesCharged = this.totalFeeChargesCharged.plus(totalFeeChargesCharged);
if (this.totalFeeChargesCharged != null) {
this.totalFeeChargesCharged = this.totalFeeChargesCharged.plus(totalFeeChargesCharged);
} else {
this.totalFeeChargesCharged = totalFeeChargesCharged;
}
}
public Money getTotalPenaltyChargesCharged() {
@@ -303,7 +323,11 @@ public final class LoanScheduleParams {
}
public void addTotalPenaltyChargesCharged(final Money totalPenaltyChargesCharged) {
this.totalPenaltyChargesCharged = this.totalPenaltyChargesCharged.plus(totalPenaltyChargesCharged);
if (this.totalPenaltyChargesCharged != null) {
this.totalPenaltyChargesCharged = this.totalPenaltyChargesCharged.plus(totalPenaltyChargesCharged);
} else {
this.totalPenaltyChargesCharged = totalPenaltyChargesCharged;
}
}
public Money getTotalRepaymentExpected() {
@@ -311,13 +335,21 @@ public final class LoanScheduleParams {
}
public void addTotalRepaymentExpected(final Money totalRepaymentExpected) {
this.totalRepaymentExpected = this.totalRepaymentExpected.plus(totalRepaymentExpected);
if (this.totalRepaymentExpected != null) {
this.totalRepaymentExpected = this.totalRepaymentExpected.plus(totalRepaymentExpected);
} else {
this.totalRepaymentExpected = totalRepaymentExpected;
}
}
public Money getReducePrincipal() {
return this.reducePrincipal;
}
public void setReducePrincipal(Money reducePrincipal) {
this.reducePrincipal = reducePrincipal;
}
public Map<LocalDate, Money> getPrincipalPortionMap() {
return this.principalPortionMap;
}
@@ -338,10 +370,18 @@ public final class LoanScheduleParams {
return this.outstandingBalance;
}
public void setOutstandingBalance(Money outstandingBalance) {
this.outstandingBalance = outstandingBalance;
}
public Money getOutstandingBalanceAsPerRest() {
return this.outstandingBalanceAsPerRest;
}
public void setOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
}
public List<LoanRepaymentScheduleInstallment> getInstallments() {
return this.installments;
}
@@ -366,12 +406,16 @@ public final class LoanScheduleParams {
return this.periodStartDate;
}
public void setPeriodStartDate(LocalDate periodStartDate) {
this.periodStartDate = periodStartDate;
}
public Money getPrincipalToBeScheduled() {
return this.principalToBeScheduled;
}
public void setPeriodNumber(int periodNumber) {
this.periodNumber = periodNumber;
public void setPrincipalToBeScheduled(Money principalToBeScheduled) {
this.principalToBeScheduled = principalToBeScheduled;
}
public void incrementPeriodNumber() {
@@ -382,56 +426,54 @@ public final class LoanScheduleParams {
this.instalmentNumber++;
}
public void setPeriodStartDate(LocalDate periodStartDate) {
this.periodStartDate = periodStartDate;
}
public void setActualRepaymentDate(LocalDate actualRepaymentDate) {
this.actualRepaymentDate = actualRepaymentDate;
}
public void setReducePrincipal(Money reducePrincipal) {
this.reducePrincipal = reducePrincipal;
}
public void addReducePrincipal(Money reducePrincipal) {
this.reducePrincipal = this.reducePrincipal.plus(reducePrincipal);
if (this.reducePrincipal != null) {
this.reducePrincipal = this.reducePrincipal.plus(reducePrincipal);
} else {
this.reducePrincipal = reducePrincipal;
}
}
public void reduceReducePrincipal(Money reducePrincipal) {
this.reducePrincipal = this.reducePrincipal.minus(reducePrincipal);
}
public void setPrincipalToBeScheduled(Money principalToBeScheduled) {
this.principalToBeScheduled = principalToBeScheduled;
if (this.reducePrincipal != null) {
this.reducePrincipal = this.reducePrincipal.minus(reducePrincipal);
}
}
public void addPrincipalToBeScheduled(Money principalToBeScheduled) {
this.principalToBeScheduled = this.principalToBeScheduled.plus(principalToBeScheduled);
}
public void setOutstandingBalance(Money outstandingBalance) {
this.outstandingBalance = outstandingBalance;
if (this.principalToBeScheduled != null) {
this.principalToBeScheduled = this.principalToBeScheduled.plus(principalToBeScheduled);
} else {
this.principalToBeScheduled = principalToBeScheduled;
}
}
public void addOutstandingBalance(Money outstandingBalance) {
this.outstandingBalance = this.outstandingBalance.plus(outstandingBalance);
if (this.outstandingBalance != null) {
this.outstandingBalance = this.outstandingBalance.plus(outstandingBalance);
} else {
this.outstandingBalance = outstandingBalance;
}
}
public void reduceOutstandingBalance(Money outstandingBalance) {
this.outstandingBalance = this.outstandingBalance.minus(outstandingBalance);
}
public void setOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
if (this.outstandingBalance != null) {
this.outstandingBalance = this.outstandingBalance.minus(outstandingBalance);
}
}
public void addOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.plus(outstandingBalanceAsPerRest);
if (this.outstandingBalanceAsPerRest != null) {
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.plus(outstandingBalanceAsPerRest);
} else {
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
}
}
public void reduceOutstandingBalanceAsPerRest(Money outstandingBalanceAsPerRest) {
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.minus(outstandingBalanceAsPerRest);
if (this.outstandingBalanceAsPerRest != null) {
this.outstandingBalanceAsPerRest = this.outstandingBalanceAsPerRest.minus(outstandingBalanceAsPerRest);
}
}
public int getLoanTermInDays() {
@@ -466,16 +508,22 @@ public final class LoanScheduleParams {
return this.unCompoundedAmount;
}
public void setUnCompoundedAmount(Money unCompoundedAmount) {
this.unCompoundedAmount = unCompoundedAmount;
}
public void addUnCompoundedAmount(Money unCompoundedAmount) {
this.unCompoundedAmount = this.unCompoundedAmount.plus(unCompoundedAmount);
if (this.unCompoundedAmount != null) {
this.unCompoundedAmount = this.unCompoundedAmount.plus(unCompoundedAmount);
} else {
this.unCompoundedAmount = unCompoundedAmount;
}
}
public void minusUnCompoundedAmount(Money unCompoundedAmount) {
this.unCompoundedAmount = this.unCompoundedAmount.minus(unCompoundedAmount);
}
public void setUnCompoundedAmount(Money unCompoundedAmount) {
this.unCompoundedAmount = unCompoundedAmount;
if (this.unCompoundedAmount != null) {
this.unCompoundedAmount = this.unCompoundedAmount.minus(unCompoundedAmount);
}
}
public boolean isFirstPeriod() {
@@ -132,18 +132,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
loanApplicationTerms.getLoanCalendar(), loanApplicationTerms.getHolidayDetailDTO(), loanApplicationTerms);
if (!scheduleParams.isPartialUpdate()) {
Money downPaymentAmount = Money.zero(currency);
if (loanApplicationTerms.isDownPaymentEnabled()) {
downPaymentAmount = Money.of(currency, MathUtil.percentageOf(scheduleParams.getOutstandingBalance().getAmount(),
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount,
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
}
}
Money calculatedAmortizableAmount = loanApplicationTerms.getPrincipal().minus(downPaymentAmount);
scheduleParams.reduceOutstandingBalance(downPaymentAmount);
Money calculatedAmortizableAmount = loanApplicationTerms.getPrincipal().minus(loanApplicationTerms.getDownPaymentAmount());
// Set Fixed Principal Amount
updateAmortization(mc, loanApplicationTerms, scheduleParams.getPeriodNumber(), calculatedAmortizableAmount);
@@ -161,9 +150,9 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
}
Money remainingPrincipalAmt = disburseAmt.minus(downPaymentAmt);
scheduleParams.setPrincipalToBeScheduled(remainingPrincipalAmt);
loanApplicationTerms.setPrincipal(remainingPrincipalAmt);
scheduleParams.setOutstandingBalance(remainingPrincipalAmt);
scheduleParams.setOutstandingBalanceAsPerRest(remainingPrincipalAmt);
loanApplicationTerms.setPrincipal(remainingPrincipalAmt);
}
}
@@ -305,7 +294,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
scheduleParams.getTotalCumulativePrincipal().minus(scheduleParams.getReducePrincipal()),
scheduleParams.getTotalCumulativeInterest(), loanApplicationTerms.getTotalInterestDue(),
scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(), scheduleParams.getOutstandingBalanceAsPerRest(),
loanApplicationTerms, scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
loanApplicationTerms, scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(loanApplicationTerms, scheduleParams),
scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, scheduledDueDate, interestRates);
// will check for EMI amount greater than interest calculated
@@ -427,10 +416,11 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
updateCompoundingDetails(periods, scheduleParams, loanApplicationTerms);
return LoanScheduleModel.from(periods, applicationCurrency, scheduleParams.getLoanTermInDays(),
scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativePrincipal().getAmount(), totalPrincipalPaid,
scheduleParams.getTotalCumulativeInterest().getAmount(), scheduleParams.getTotalFeeChargesCharged().getAmount(),
scheduleParams.getTotalPenaltyChargesCharged().getAmount(), scheduleParams.getTotalRepaymentExpected().getAmount(),
totalOutstanding);
scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount()),
scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount()).getAmount(),
totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(),
scheduleParams.getTotalFeeChargesCharged().getAmount(), scheduleParams.getTotalPenaltyChargesCharged().getAmount(),
scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
}
private void updateCompoundingDetails(final Collection<LoanScheduleModelPeriod> periods, final LoanScheduleParams params,
@@ -510,7 +500,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
updateCompoundingMap(loanApplicationTerms, holidayDetailDTO, scheduleParams, lastRestDate, scheduledDueDate);
// update outstanding balance for interest calculation
updateOutstandingBalanceAsPerRest(scheduleParams, scheduledDueDate);
updateOutstandingBalanceAsPerRest(loanApplicationTerms, scheduleParams, scheduledDueDate);
}
private LoanScheduleModelPeriod handleRecalculationForTransactions(final MathContext mc,
@@ -630,8 +620,8 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
interestCalculationGraceOnRepaymentPeriodFraction, scheduleParams.getTotalCumulativePrincipal(),
scheduleParams.getTotalCumulativeInterest(), totalInterestChargedForFullLoanTerm,
lastTotalOutstandingInterestPaymentDueToGrace, scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams), scheduleParams.getCompoundingMap(),
periodStartDateApplicableForInterest, calculateTill, interestRates);
scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(loanApplicationTerms, scheduleParams),
scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, calculateTill, interestRates);
loanApplicationTerms.updateAnnualNominalInterestRate(currentInterestRate);
// applies charges for the period
@@ -767,7 +757,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
scheduleParams.getTotalCumulativeInterest(), totalInterestChargedForFullLoanTerm,
scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(),
scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(loanApplicationTerms, scheduleParams),
scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, transactionDate,
interestRates);
interestForCurrentInstallment = principalInterestForThisPeriod.interest();
@@ -805,7 +795,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
// update outstanding balance for interest
// calculation as per the rest
updateOutstandingBalanceAsPerRest(scheduleParams, transactionDate);
updateOutstandingBalanceAsPerRest(loanApplicationTerms, scheduleParams, transactionDate);
// handle cumulative fields
scheduleParams.addLoanTermInDays(periodDays);
@@ -898,9 +888,9 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
scheduleParams.getTotalCumulativeInterest(), totalInterestChargedForFullLoanTerm,
scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(),
scheduleParams.getOutstandingBalanceAsPerRest(), loanApplicationTerms,
scheduleParams.getPeriodNumber(), mc, mergeVariationsToMap(scheduleParams),
scheduleParams.getCompoundingMap(), periodStartDateApplicableForInterest, transactionDate,
interestRates);
scheduleParams.getPeriodNumber(), mc,
mergeVariationsToMap(loanApplicationTerms, scheduleParams), scheduleParams.getCompoundingMap(),
periodStartDateApplicableForInterest, transactionDate, interestRates);
if (!principalInterestForThisPeriod.interest()
.plus(principalInterestForThisPeriod.interestPaymentDueToGrace())
.plus(scheduleParams.getOutstandingBalance()).isGreaterThan(unprocessed)) {
@@ -971,11 +961,37 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
scheduleParams.addTotalCumulativePrincipal(unprocessed.plus(addToPrincipal));
}
private void updateOutstandingBalanceAsPerRest(final LoanScheduleParams scheduleParams, final LocalDate scheduledDueDate) {
private void updateOutstandingBalanceAsPerRest(LoanApplicationTerms loanApplicationTerms, final LoanScheduleParams scheduleParams,
final LocalDate scheduledDueDate) {
scheduleParams.setOutstandingBalanceAsPerRest(updateBalanceForInterestCalculation(scheduleParams.getPrincipalPortionMap(),
scheduledDueDate, scheduleParams.getOutstandingBalanceAsPerRest(), false));
scheduleParams.setOutstandingBalanceAsPerRest(updateBalanceForInterestCalculation(scheduleParams.getDisburseDetailMap(),
scheduledDueDate, scheduleParams.getOutstandingBalanceAsPerRest(), true));
scheduledDueDate, scheduleParams.getOutstandingBalanceAsPerRest()));
scheduleParams.setOutstandingBalanceAsPerRest(calculateOutstandingBalanceAsPerRest(loanApplicationTerms,
scheduleParams.getDisburseDetailMap(), scheduledDueDate, scheduleParams.getOutstandingBalanceAsPerRest()));
}
private Money calculateOutstandingBalanceAsPerRest(LoanApplicationTerms loanApplicationTerms, Map<LocalDate, Money> disburseDetailMap,
LocalDate scheduledDueDate, Money outstandingBalance) {
List<LocalDate> removeFromMap = new ArrayList<>();
for (Map.Entry<LocalDate, Money> entry : disburseDetailMap.entrySet()) {
if (!DateUtils.isAfter(entry.getKey(), scheduledDueDate)) {
Money downPaymentAmount = Money.zero(loanApplicationTerms.getCurrency());
if (loanApplicationTerms.isDownPaymentEnabled()) {
downPaymentAmount = Money.of(loanApplicationTerms.getCurrency(), MathUtil.percentageOf(entry.getValue().getAmount(),
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount,
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
}
}
outstandingBalance = outstandingBalance.plus(entry.getValue()).minus(downPaymentAmount);
removeFromMap.add(entry.getKey());
}
}
for (LocalDate date : removeFromMap) {
disburseDetailMap.remove(date);
}
return outstandingBalance;
}
/**
@@ -1305,7 +1321,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
} else {
principalToBeScheduled = loanApplicationTerms.getPrincipal();
}
return principalToBeScheduled;
return principalToBeScheduled.minus(loanApplicationTerms.getDownPaymentAmount());
}
private boolean updateFixedInstallmentAmount(final MathContext mc, final LoanApplicationTerms loanApplicationTerms, int periodNumber,
@@ -1389,8 +1405,8 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
getPaymentPeriodsInOneYearCalculator(), interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(),
totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms,
periodNumberTemp, mc, mergeVariationsToMap(params), params.getCompoundingMap(), params.getPeriodStartDate(),
transactionDate, applicableVariations);
periodNumberTemp, mc, mergeVariationsToMap(loanApplicationTerms, params), params.getCompoundingMap(),
params.getPeriodStartDate(), transactionDate, applicableVariations);
Money interest = principalInterestForThisPeriod.interest();
totalInterest = totalInterest.plus(interest);
@@ -1421,7 +1437,7 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
outstanding = outstanding.zero();
} else {
outstanding = updateBalanceForInterestCalculation(params.getPrincipalPortionMap(), params.getPeriodStartDate(),
outstanding, false);
outstanding);
}
if (params.getLatePaymentMap().isEmpty() && outstanding.isZero()) {
break;
@@ -1432,8 +1448,8 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
getPaymentPeriodsInOneYearCalculator(), interestCalculationGraceOnRepaymentPeriodFraction, totalInterest.zero(),
totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms,
periodNumberTemp, mc, mergeVariationsToMap(params), params.getCompoundingMap(), params.getPeriodStartDate(),
params.getActualRepaymentDate(), applicableVariations);
periodNumberTemp, mc, mergeVariationsToMap(loanApplicationTerms, params), params.getCompoundingMap(),
params.getPeriodStartDate(), params.getActualRepaymentDate(), applicableVariations);
Money interest = principalInterestForThisPeriod.interest();
totalInterest = totalInterest.plus(interest);
@@ -1562,16 +1578,12 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
* Identifies all the past date principal changes and apply them on outstanding balance for future calculations
*/
private Money updateBalanceForInterestCalculation(final Map<LocalDate, Money> principalPortionMap, final LocalDate scheduledDueDate,
final Money outstandingBalanceAsPerRest, boolean addMapDetails) {
final Money outstandingBalanceAsPerRest) {
List<LocalDate> removeFromPrincipalPortionMap = new ArrayList<>();
Money outstandingBalance = outstandingBalanceAsPerRest;
for (Map.Entry<LocalDate, Money> principal : principalPortionMap.entrySet()) {
if (!DateUtils.isAfter(principal.getKey(), scheduledDueDate)) {
if (addMapDetails) {
outstandingBalance = outstandingBalance.plus(principal.getValue());
} else {
outstandingBalance = outstandingBalance.minus(principal.getValue());
}
outstandingBalance = outstandingBalance.minus(principal.getValue());
removeFromPrincipalPortionMap.add(principal.getKey());
}
}
@@ -1852,13 +1864,22 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
* as per rest) changes to single map for interest calculation
*
*/
private TreeMap<LocalDate, Money> mergeVariationsToMap(final LoanScheduleParams params) {
private TreeMap<LocalDate, Money> mergeVariationsToMap(LoanApplicationTerms loanApplicationTerms, final LoanScheduleParams params) {
TreeMap<LocalDate, Money> map = new TreeMap<>(params.getLatePaymentMap());
for (Map.Entry<LocalDate, Money> mapEntry : params.getDisburseDetailMap().entrySet()) {
Money value = mapEntry.getValue();
if (map.containsKey(mapEntry.getKey())) {
value = value.plus(map.get(mapEntry.getKey()));
}
if (loanApplicationTerms.isDownPaymentEnabled()) {
Money downPaymentAmount = Money.of(loanApplicationTerms.getCurrency(),
MathUtil.percentageOf(value.getAmount(), loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount,
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
}
value = value.minus(downPaymentAmount);
}
map.put(mapEntry.getKey(), value);
}
@@ -2235,33 +2256,30 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
LocalDate periodStartDate = RepaymentStartDateType.DISBURSEMENT_DATE.equals(loanApplicationTerms.getRepaymentStartDateType())
? loanApplicationTerms.getExpectedDisbursementDate()
: loanApplicationTerms.getSubmittedOnDate();
Money downPaymentAmount = Money.zero(currency);
if (loanApplicationTerms.isDownPaymentEnabled()) {
downPaymentAmount = Money.of(currency, MathUtil.percentageOf(loanScheduleParams.getOutstandingBalance().getAmount(),
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount,
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
}
}
Money calculatedAmortizableAmount = principalToBeScheduled.minus(downPaymentAmount);
loanScheduleParams.setOutstandingBalance(calculatedAmortizableAmount);
// Set fixed Amortization Amounts(either EMI or Principal )
updateAmortization(mc, loanApplicationTerms, periodNumber, calculatedAmortizableAmount);
updateAmortization(mc, loanApplicationTerms, periodNumber, outstandingBalance);
// count periods without interest grace to exclude for flat loan
// calculations
final Map<LocalDate, Money> disburseDetailMap = new HashMap<>();
if (loanApplicationTerms.isMultiDisburseLoan()) {
// fetches the first tranche amount and also updates other
// tranche
// details to map
BigDecimal disburseAmt = getDisbursementAmount(loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(),
disburseDetailMap, true);
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
outstandingBalanceAsPerRest = outstandingBalance;
principalToBeScheduled = principalToBeScheduled.zero().plus(disburseAmt);
/* fetches the first tranche amount and also updates other tranche details to map */
Money disburseAmt = Money.of(currency,
getDisbursementAmount(loanApplicationTerms, loanApplicationTerms.getExpectedDisbursementDate(), disburseDetailMap,
loanScheduleParams.applyInterestRecalculation()));
Money downPaymentAmt = Money.zero(currency);
if (loanApplicationTerms.isDownPaymentEnabled()) {
downPaymentAmt = Money.of(currency, MathUtil.percentageOf(disburseAmt.getAmount(),
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19));
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt, loanApplicationTerms.getInstallmentAmountInMultiplesOf());
}
}
Money remainingPrincipalAmt = disburseAmt.minus(downPaymentAmt);
outstandingBalance = remainingPrincipalAmt;
outstandingBalanceAsPerRest = remainingPrincipalAmt;
principalToBeScheduled = remainingPrincipalAmt;
}
int loanTermInDays = 0;
@@ -2279,6 +2297,12 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
// This will create the recalculation details by applying the
// transactions
for (LoanRepaymentScheduleInstallment installment : processInstallmentsInstallments) {
if (installment.isDownPayment()) {
instalmentNumber++;
periods.add(createLoanScheduleModelDownPaymentPeriod(installment, outstandingBalance));
newRepaymentScheduleInstallments.add(installment);
continue;
}
// this will generate the next schedule due date and allows to
// process the installment only if recalculate from date is
// greater than due date
@@ -2442,9 +2466,9 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
// update outstanding balance for interest calculation
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(principalPortionMap, installment.getDueDate(),
outstandingBalanceAsPerRest, false);
outstandingBalanceAsPerRest = updateBalanceForInterestCalculation(disburseDetailMap, installment.getDueDate(),
outstandingBalanceAsPerRest, true);
outstandingBalanceAsPerRest);
outstandingBalanceAsPerRest = calculateOutstandingBalanceAsPerRest(loanApplicationTerms, disburseDetailMap,
installment.getDueDate(), outstandingBalanceAsPerRest);
// updates the map with over due amounts
updateLatePaymentsToMap(loanApplicationTerms, holidayDetailDTO, currency, latePaymentMap, lastInstallmentDate,
newRepaymentScheduleInstallments, true, lastRestDate);
@@ -2692,6 +2716,13 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch
return installment;
}
private LoanScheduleModelPeriod createLoanScheduleModelDownPaymentPeriod(final LoanRepaymentScheduleInstallment installment,
final Money outstandingPrincipal) {
final MonetaryCurrency currency = outstandingPrincipal.getCurrency();
return LoanScheduleModelDownPaymentPeriod.downPayment(installment.getInstallmentNumber(), installment.getDueDate(),
installment.getPrincipal(currency), outstandingPrincipal);
}
private LoanScheduleModelPeriod createLoanScheduleModelPeriod(final LoanRepaymentScheduleInstallment installment,
final Money outstandingPrincipal) {
final MonetaryCurrency currency = outstandingPrincipal.getCurrency();
@@ -188,10 +188,11 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
final BigDecimal totalOutstanding = BigDecimal.ZERO;
return LoanScheduleModel.from(periods, applicationCurrency, scheduleParams.getLoanTermInDays(),
scheduleParams.getPrincipalToBeScheduled(), scheduleParams.getTotalCumulativePrincipal().getAmount(), totalPrincipalPaid,
scheduleParams.getTotalCumulativeInterest().getAmount(), scheduleParams.getTotalFeeChargesCharged().getAmount(),
scheduleParams.getTotalPenaltyChargesCharged().getAmount(), scheduleParams.getTotalRepaymentExpected().getAmount(),
totalOutstanding);
scheduleParams.getPrincipalToBeScheduled().plus(loanApplicationTerms.getDownPaymentAmount()),
scheduleParams.getTotalCumulativePrincipal().plus(loanApplicationTerms.getDownPaymentAmount()).getAmount(),
totalPrincipalPaid, scheduleParams.getTotalCumulativeInterest().getAmount(),
scheduleParams.getTotalFeeChargesCharged().getAmount(), scheduleParams.getTotalPenaltyChargesCharged().getAmount(),
scheduleParams.getTotalRepaymentExpected().getAmount(), totalOutstanding);
}
@Override
@@ -23,7 +23,6 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
@@ -675,8 +674,7 @@ public class LoanScheduleAssembler {
final Set<LoanCharge> loanCharges = this.loanChargeAssembler.fromParsedJson(element, disbursementDetails);
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mc = new MathContext(8, roundingMode);
final MathContext mc = MoneyHelper.getMathContext();
HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),
@@ -703,8 +701,8 @@ public class LoanScheduleAssembler {
public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId,
Loan loan, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
final LocalDate rescheduleFrom) {
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mc = new MathContext(8, roundingMode);
final MathContext mc = MoneyHelper.getMathContext();
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
@@ -723,8 +721,8 @@ public class LoanScheduleAssembler {
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) {
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),
loanApplicationTerms.getInterestMethod());
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mc = new MathContext(8, roundingMode);
final MathContext mc = MoneyHelper.getMathContext();
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId,
@@ -19,7 +19,6 @@
package org.apache.fineract.portfolio.loanaccount.rescheduleloan.service;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -127,8 +126,8 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
}
loanApplicationTerms.getLoanTermVariations().updateLoanTermVariationsData(loanTermVariationsData);
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mathContext = new MathContext(8, roundingMode);
final MathContext mathContext = MoneyHelper.getMathContext();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy());
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.rescheduleloan.service;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
@@ -432,8 +431,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
* loanTermVariation.setApplicableFromDate(adjustedDate); } } }
*/
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mathContext = new MathContext(8, roundingMode);
final MathContext mathContext = MoneyHelper.getMathContext();
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.loanRepaymentScheduleTransactionProcessorFactory
.determineProcessor(loan.transactionProcessingStrategy());
final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getLoanScheduleType(),