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
@@ -41,7 +41,6 @@ import jakarta.persistence.Version;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
@@ -2823,8 +2822,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
public LoanScheduleModel regenerateScheduleModel(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mc = new MathContext(8, roundingMode);
final MathContext mc = MoneyHelper.getMathContext();
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
LoanScheduleGenerator loanScheduleGenerator;
@@ -5762,8 +5760,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory()
.create(this.loanRepaymentScheduleDetail.getLoanScheduleType(), interestMethod);
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mc = new MathContext(19, roundingMode);
final MathContext mc = MoneyHelper.getMathContext();
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO);
@@ -5778,8 +5775,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
LoanRepaymentScheduleInstallment installment = null;
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
final MathContext mc = new MathContext(8, roundingMode);
final MathContext mc = MoneyHelper.getMathContext();
final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod();
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(scheduleGeneratorDTO);
@@ -565,7 +565,7 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
BigDecimal percentageOf = BigDecimal.ZERO;
if (isGreaterThanZero(value)) {
final MathContext mc = new MathContext(8, MoneyHelper.getRoundingMode());
final MathContext mc = MoneyHelper.getMathContext();
final BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100L), mc);
percentageOf = value.multiply(multiplicand, mc);
}
@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
@@ -212,6 +213,7 @@ public final class LoanApplicationTerms {
private LocalDate newScheduledDueDateStart;
private boolean isDownPaymentEnabled;
private BigDecimal disbursedAmountPercentageForDownPayment;
private Money downPaymentAmount;
private boolean isAutoRepaymentForDownPaymentEnabled;
private RepaymentStartDateType repaymentStartDateType;
@@ -450,6 +452,15 @@ public final class LoanApplicationTerms {
this.isPrincipalCompoundingDisabledForOverdueLoans = isPrincipalCompoundingDisabledForOverdueLoans;
this.isDownPaymentEnabled = isDownPaymentEnabled;
this.disbursedAmountPercentageForDownPayment = disbursedAmountPercentageForDownPayment;
this.downPaymentAmount = Money.zero(getCurrency());
if (isDownPaymentEnabled) {
this.downPaymentAmount = Money.of(getCurrency(),
MathUtil.percentageOf(getPrincipal().getAmount(), getDisbursedAmountPercentageForDownPayment(), 19));
if (getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount, getInstallmentAmountInMultiplesOf());
}
}
this.isAutoRepaymentForDownPaymentEnabled = isAutoRepaymentForDownPaymentEnabled;
this.repaymentStartDateType = repaymentStartDateType;
this.submittedOnDate = submittedOnDate;
@@ -1817,4 +1828,8 @@ public final class LoanApplicationTerms {
public LoanScheduleType getLoanScheduleType() {
return loanScheduleType;
}
public Money getDownPaymentAmount() {
return downPaymentAmount;
}
}
@@ -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(),
@@ -124,7 +124,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -192,7 +192,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -259,7 +259,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -327,7 +327,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -414,7 +414,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -498,7 +498,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -580,7 +580,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -666,7 +666,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -735,7 +735,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -818,7 +818,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -878,7 +878,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -947,7 +947,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1030,7 +1030,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1115,7 +1115,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1197,7 +1197,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1253,7 +1253,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1309,7 +1309,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("15 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1365,7 +1365,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("20 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1458,7 +1458,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("20 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1572,7 +1572,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
runAt("20 February 2023", () -> {
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1645,7 +1645,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("500", "15", "4", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1724,7 +1724,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("500", "15", "4", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1815,7 +1815,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("500", "15", "4", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1881,7 +1881,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("500", "15", "4", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -1960,7 +1960,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("500", "15", "4", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.VERTICAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(500.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
@@ -2065,15 +2065,16 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
assertNotNull(localLoanProductId);
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
() -> applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(500.0), 45, 15, 3, 0,
"01 January 2023", "01 January 2023", FineractStyleLoanRepaymentScheduleTransactionProcessor.STRATEGY_CODE,
LoanScheduleType.PROGRESSIVE.name(), LoanScheduleProcessingType.VERTICAL.name()));
() -> applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(500.0), 45, 15, 3,
BigDecimal.ZERO, "01 January 2023", "01 January 2023",
FineractStyleLoanRepaymentScheduleTransactionProcessor.STRATEGY_CODE, LoanScheduleType.PROGRESSIVE.name(),
LoanScheduleProcessingType.VERTICAL.name()));
assertEquals(400, exception.getResponse().code());
assertTrue(exception.getMessage().contains("supported.only.with.advanced.payment.allocation.strategy"));
exception = assertThrows(CallFailedRuntimeException.class,
() -> applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(500.0), 45, 15, 3, 0,
"01 January 2023", "01 January 2023",
() -> applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(500.0), 45, 15, 3,
BigDecimal.ZERO, "01 January 2023", "01 January 2023",
AdvancedPaymentScheduleTransactionProcessor.ADVANCED_PAYMENT_ALLOCATION_STRATEGY,
LoanScheduleType.PROGRESSIVE.name(), LoanScheduleProcessingType.HORIZONTAL.name()));
assertEquals(400, exception.getResponse().code());
@@ -2099,7 +2100,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
assertNotNull(localLoanProductId);
PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), 45,
15, 3, 0, "01 January 2023", "01 January 2023");
15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 16), 333.33, 0.0, 333.33, 0.0, 0.0);
@@ -2128,8 +2129,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 2, 15), 333.34, 0.0, 333.34, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), 90, 15, 6, 0,
"01 January 2023", "01 January 2023");
loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), 90, 15, 6,
BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 16), 166.67, 0.0, 166.67, 0.0, 0.0);
@@ -2187,7 +2188,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
assertNotNull(localLoanProductId);
PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), 45,
15, 3, 0, "01 January 2023", "01 January 2023");
15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 16), 333.33, 0.0, 333.33, 0.0, 0.0);
@@ -2216,8 +2217,8 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 2, 15), 333.34, 0.0, 333.34, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), 90, 15, 6, 0,
"01 January 2023", "01 January 2023");
loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(1000), 90, 15, 6,
BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 16), 166.67, 0.0, 166.67, 0.0, 0.0);
@@ -2275,7 +2276,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
assertNotNull(localLoanProductId);
PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40.50),
45, 15, 3, 0, "01 January 2023", "01 January 2023");
45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 10.12, 0.0, 10.12, 0.0, 0.0);
@@ -2326,7 +2327,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
assertNotNull(localLoanProductId);
PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, BigDecimal.valueOf(40.50),
45, 15, 3, 0, "01 January 2023", "01 January 2023");
45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 10.12, 0.0, 10.12, 0.0, 0.0);
@@ -2380,7 +2381,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 September 2023", "01 September 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 September 2023", "01 September 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -2486,7 +2487,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.VERTICAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 September 2023", "01 September 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 September 2023", "01 September 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -2591,7 +2592,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 September 2023", "01 September 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 September 2023", "01 September 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -2696,7 +2697,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.VERTICAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 September 2023", "01 September 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 September 2023", "01 September 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -2795,7 +2796,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -2881,7 +2882,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -2970,7 +2971,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", false, LoanScheduleType.PROGRESSIVE,
LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId,
BigDecimal.valueOf(1000.0), 45, 15, 3, 0, "01 January 2023", "01 January 2023");
BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -3205,7 +3206,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final int interestRate,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate, String transactionProcessorCode, String loanScheduleType,
String loanScheduleProcessingType) {
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
@@ -3220,7 +3221,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final BigDecimal principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final int interestRate,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate) {
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
return applyForLoanApplication(clientId, loanProductId, principal, loanTermFrequency, repaymentAfterEvery, numberOfRepayments,
@@ -128,14 +128,14 @@ public abstract class BaseLoanIntegrationTest {
.useBorrowerCycle(false)//
.minPrincipal(100.0)//
.principal(1000.0)//
.maxPrincipal(10000.0)//
.maxPrincipal(100000.0)//
.minNumberOfRepayments(1)//
.numberOfRepayments(1)//
.maxNumberOfRepayments(30)//
.isLinkedToFloatingInterestRates(false)//
.minInterestRatePerPeriod((double) 0)//
.interestRatePerPeriod((double) 0)//
.maxInterestRatePerPeriod((double) 0)//
.maxInterestRatePerPeriod((double) 100)//
.interestRateFrequencyType(2)//
.repaymentEvery(30)//
.repaymentFrequencyType(0L)//
@@ -412,8 +412,8 @@ public abstract class BaseLoanIntegrationTest {
PostLoansRequest postLoansRequest = new PostLoansRequest().clientId(clientId).productId(loanProductId)
.expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN)
.transactionProcessingStrategyCode(DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY)
.locale("en").submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(0)
.interestCalculationPeriodType(1).interestType(0).repaymentFrequencyType(0).repaymentEvery(30).repaymentFrequencyType(0)
.locale("en").submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(BigDecimal.ZERO)
.interestCalculationPeriodType(1).interestType(0).repaymentEvery(30).repaymentFrequencyType(0)
.numberOfRepayments(numberOfRepayments).loanTermFrequency(numberOfRepayments * 30).loanTermFrequencyType(0)
.maxOutstandingLoanBalance(BigDecimal.valueOf(amount)).principal(BigDecimal.valueOf(amount)).loanType("individual");
if (customizer != null) {
@@ -422,9 +422,9 @@ public abstract class BaseLoanIntegrationTest {
return postLoansRequest;
}
protected PostLoansLoanIdRequest approveLoanRequest(Double amount) {
protected PostLoansLoanIdRequest approveLoanRequest(Double amount, String approvalDate) {
return new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount)).dateFormat(DATETIME_PATTERN)
.approvedOnDate("01 January 2023").locale("en");
.approvedOnDate(approvalDate).locale("en");
}
protected Long applyAndApproveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount,
@@ -438,7 +438,7 @@ public abstract class BaseLoanIntegrationTest {
.applyLoan(applyLoanRequest(clientId, loanProductId, loanDisbursementDate, amount, numberOfRepayments, customizer));
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
approveLoanRequest(amount));
approveLoanRequest(amount, loanDisbursementDate));
return approvedLoanResult.getLoanId();
}
@@ -8041,9 +8041,9 @@ public class ClientLoanIntegrationTest {
return LOAN_TRANSACTION_HELPER.applyLoan(new PostLoansRequest().clientId(clientId.longValue()).productId(loanProductId)
.expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN)
.transactionProcessingStrategyCode(repaymentStrategyCode).locale("en").submittedOnDate(loanDisbursementDate)
.amortizationType(1).interestRatePerPeriod(0).interestCalculationPeriodType(1).interestType(0).repaymentFrequencyType(0)
.repaymentEvery(30).repaymentFrequencyType(0).numberOfRepayments(1).loanTermFrequency(30).loanTermFrequencyType(0)
.principal(BigDecimal.valueOf(1000.0)).loanType("individual"));
.amortizationType(1).interestRatePerPeriod(BigDecimal.ZERO).interestCalculationPeriodType(1).interestType(0)
.repaymentFrequencyType(0).repaymentEvery(30).repaymentFrequencyType(0).numberOfRepayments(1).loanTermFrequency(30)
.loanTermFrequencyType(0).principal(BigDecimal.valueOf(1000.0)).loanType("individual"));
}
private PostLoanProductsRequest createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct() {
@@ -935,10 +935,11 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
"modify",
new PutLoansLoanIdRequest().submittedOnDate("31 August 2022").dateFormat("dd MMMM yyyy").locale("en")
.loanType("individual").productId(loanProductID.longValue()).clientId(client.getClientId()).interestType(0)
.interestCalculationPeriodType(1).interestRatePerPeriod(0).isEqualAmortization(false).loanTermFrequency(30)
.loanTermFrequencyType(0).maxOutstandingLoanBalance(10000L).numberOfRepayments(1).principal(10000L)
.repaymentEvery(30).repaymentFrequencyType(0).transactionProcessingStrategyCode("mifos-standard-strategy")
.expectedDisbursementDate("2 September 2022").amortizationType(1));
.interestCalculationPeriodType(1).interestRatePerPeriod(BigDecimal.ZERO).isEqualAmortization(false)
.loanTermFrequency(30).loanTermFrequencyType(0).maxOutstandingLoanBalance(10000L).numberOfRepayments(1)
.principal(10000L).repaymentEvery(30).repaymentFrequencyType(0)
.transactionProcessingStrategyCode("mifos-standard-strategy").expectedDisbursementDate("2 September 2022")
.amortizationType(1));
assertEquals(loanExternalIdStr2, modifyLoanApplicationResult.getResourceExternalId());
DeleteLoansLoanIdResponse deleteLoanApplicationResult = this.loanTransactionHelper.deleteLoanApplication(loanExternalIdStr2);
@@ -163,8 +163,8 @@ public class LoanChargePaymentWithAdvancedPaymentAllocationTest {
savingsAccountHelper.depositToSavingsAccount(savingsId, "10000", startDate, CommonConstants.RESPONSE_RESOURCE_ID);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId, 1000L, 45, 15, 3, 0,
"01 January 2023", "01 January 2023");
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId, 1000L, 45, 15, 3,
BigDecimal.ZERO, "01 January 2023", "01 January 2023");
int loanId = loanResponse.getLoanId().intValue();
@@ -266,7 +266,7 @@ public class LoanChargePaymentWithAdvancedPaymentAllocationTest {
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final Long principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final int interestRate,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate) {
log.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
return loanTransactionHelper.applyLoan(new PostLoansRequest().clientId(clientId).productId(loanProductId.longValue())
@@ -71,7 +71,7 @@ public class LoanInstallmentMultiplesOfTest extends BaseLoanIntegrationTest {
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest);
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
approveLoanRequest(amount));
approveLoanRequest(amount, "01 January 2023"));
Long loanId = approvedLoanResult.getLoanId();
@@ -130,12 +130,12 @@ public class LoanInstallmentMultiplesOfTest extends BaseLoanIntegrationTest {
.interestType(interestType)//
.amortizationType(amortizationType)//
.interestCalculationPeriodType(SAME_AS_REPAYMENT_PERIOD)//
.interestRatePerPeriod(12);
.interestRatePerPeriod(BigDecimal.valueOf(12));
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest);
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
approveLoanRequest(amount));
approveLoanRequest(amount, "01 January 2023"));
Long loanId = approvedLoanResult.getLoanId();
@@ -193,12 +193,12 @@ public class LoanInstallmentMultiplesOfTest extends BaseLoanIntegrationTest {
.interestType(interestType)//
.amortizationType(amortizationType)//
.interestCalculationPeriodType(SAME_AS_REPAYMENT_PERIOD)//
.interestRatePerPeriod(12);
.interestRatePerPeriod(BigDecimal.valueOf(12));
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest);
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
approveLoanRequest(amount));
approveLoanRequest(amount, "01 January 2023"));
Long loanId = approvedLoanResult.getLoanId();
@@ -36,13 +36,15 @@ import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(LoanTestLifecycleExtension.class)
public class LoanRescheduleTestWithDownpayment extends BaseLoanIntegrationTest {
public static final BigDecimal DOWN_PAYMENT_PERCENTAGE = new BigDecimal(25);
public static final BigDecimal DOWN_PAYMENT_PERCENTAGE_20 = new BigDecimal(20);
public static final BigDecimal DOWN_PAYMENT_PERCENTAGE_25 = new BigDecimal(25);
public static final BigDecimal DOWN_PAYMENT_PERCENTAGE_33 = new BigDecimal(33);
private final LoanRescheduleRequestHelper loanRescheduleRequestHelper = new LoanRescheduleRequestHelper(this.requestSpec,
this.responseSpec);
@Test
public void testRescheduleWithDownpayment() {
public void testRescheduleWithDownPayment() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
@@ -105,6 +107,342 @@ public class LoanRescheduleTestWithDownpayment extends BaseLoanIntegrationTest {
});
}
@Test
public void testRescheduleAddExtraInstallmentsWithDownPayment() {
runAt("01 November 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProductWith33PctDownPayment(true, true);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, "02 October 2023", 1000.0, 3);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, false, "02 October 2023"), //
installment(223.33, false, "01 November 2023"), //
installment(223.33, false, "01 December 2023"), //
installment(223.34, false, "31 December 2023") //
);
// 1st Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "02 October 2023");
// verify transactions
verifyTransactions(loanId, //
transaction(330.0, "Down Payment", "02 October 2023"), //
transaction(1000.0, "Disbursement", "02 October 2023") //
);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, true, "02 October 2023"), //
installment(223.33, false, "01 November 2023"), //
installment(223.33, false, "01 December 2023"), //
installment(223.34, false, "31 December 2023") //
);
String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnInterest(null).updateGraceOnPrincipal(null)
.updateExtraTerms("2").updateNewInterestRate(null).updateRescheduleFromDate("01 November 2023")
.updateAdjustedDueDate(null).updateSubmittedOnDate("01 November 2023").updateRescheduleReasonId("1")
.build(loanId.toString());
Integer loanRescheduleRequest = loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
requestJSON = new LoanRescheduleRequestTestBuilder().updateSubmittedOnDate("01 November 2023")
.getApproveLoanRescheduleRequestJSON();
Integer approveLoanRescheduleRequest = loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequest,
requestJSON);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, true, "02 October 2023"), //
installment(134.0, false, "01 November 2023"), //
installment(134.0, false, "01 December 2023"), //
installment(134.0, false, "31 December 2023"), //
installment(134.0, false, "30 January 2024"), //
installment(134.0, false, "29 February 2024") //
);
});
}
@Test
public void testRescheduleAddExtraInstallmentsMultipleDisbursementWithDownPayment() {
runAt("31 December 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProductWith33PctDownPayment(true, true);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, "02 October 2023", 1000.0, 3);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, false, "02 October 2023"), //
installment(223.33, false, "01 November 2023"), //
installment(223.33, false, "01 December 2023"), //
installment(223.34, false, "31 December 2023") //
);
// 1st Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "02 October 2023");
// verify transactions
verifyTransactions(loanId, //
transaction(330.0, "Down Payment", "02 October 2023"), //
transaction(1000.0, "Disbursement", "02 October 2023") //
);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, true, "02 October 2023"), //
installment(223.33, false, "01 November 2023"), //
installment(223.33, false, "01 December 2023"), //
installment(223.34, false, "31 December 2023") //
);
// 2nd Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(200.00), "02 December 2023");
// verify transactions
verifyTransactions(loanId, //
transaction(330.0, "Down Payment", "02 October 2023"), //
transaction(1000.0, "Disbursement", "02 October 2023"), //
transaction(200.0, "Disbursement", "02 December 2023"), //
transaction(66.0, "Down Payment", "02 December 2023") //
);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, true, "02 October 2023"), //
installment(268.0, false, "01 November 2023"), //
installment(268.0, false, "01 December 2023"), //
installment(200.0, null, "02 December 2023"), //
installment(66.0, false, "02 December 2023"), //
installment(268.0, false, "31 December 2023") //
);
String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnInterest(null).updateGraceOnPrincipal(null)
.updateExtraTerms("2").updateNewInterestRate(null).updateRescheduleFromDate("31 December 2023")
.updateAdjustedDueDate(null).updateSubmittedOnDate("31 December 2023").updateRescheduleReasonId("1")
.build(loanId.toString());
Integer loanRescheduleRequest = loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
requestJSON = new LoanRescheduleRequestTestBuilder().updateSubmittedOnDate("31 December 2023")
.getApproveLoanRescheduleRequestJSON();
Integer approveLoanRescheduleRequest = loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequest,
requestJSON);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(1000.0, null, "02 October 2023"), //
installment(330.0, true, "02 October 2023"), //
installment(268, false, "01 November 2023"), //
installment(268, false, "01 December 2023"), //
installment(200.0, null, "02 December 2023"), //
installment(66.0, false, "02 December 2023"), //
installment(89.33, false, "31 December 2023"), //
installment(89.33, false, "30 January 2024"), //
installment(89.34, false, "29 February 2024") //
);
});
}
@Test
public void testRescheduleAddExtraInstallmentsDisbursementWithDownPaymentWithInterest() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProductWith20PctDownPaymentWithDecliningBalanceInterest(true, true, 5.0);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 January 2023", 15000.0, 6, postLoansRequest -> {
postLoansRequest.interestRatePerPeriod(BigDecimal.valueOf(5));
postLoansRequest.interestRatePerPeriod(BigDecimal.valueOf(5));
postLoansRequest.repaymentEvery(1);
postLoansRequest.loanTermFrequencyType(RepaymentFrequencyType.MONTHS);
postLoansRequest.repaymentFrequencyType(RepaymentFrequencyType.MONTHS);
postLoansRequest.loanTermFrequency(6);
});
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(3000.00, false, "01 January 2023"), //
installment(1764.21, false, "01 February 2023"), //
installment(1852.42, false, "01 March 2023"), //
installment(1945.04, false, "01 April 2023"), //
installment(2042.29, false, "01 May 2023"), //
installment(2144.41, false, "01 June 2023"), //
installment(2251.63, false, "01 July 2023") //
);
// 1st Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(12000.00), "01 January 2023");
// verify transactions
verifyTransactions(loanId, //
transaction(2400.0, "Down Payment", "01 January 2023"), //
transaction(12000.0, "Disbursement", "01 January 2023") //
);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(2400.00, true, "01 January 2023"), //
installment(1411.37, false, "01 February 2023"), //
installment(1481.94, false, "01 March 2023"), //
installment(1556.04, false, "01 April 2023"), //
installment(1633.84, false, "01 May 2023"), //
installment(1715.53, false, "01 June 2023"), //
installment(1801.28, false, "01 July 2023") //
);
updateBusinessDate("01 June 2023");
String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnInterest(null).updateGraceOnPrincipal(null)
.updateExtraTerms("2").updateNewInterestRate(null).updateRescheduleFromDate("01 June 2023").updateAdjustedDueDate(null)
.updateSubmittedOnDate("01 June 2023").updateRescheduleReasonId("1").build(loanId.toString());
Integer loanRescheduleRequest = loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
requestJSON = new LoanRescheduleRequestTestBuilder().updateSubmittedOnDate("01 June 2023")
.getApproveLoanRescheduleRequestJSON();
Integer approveLoanRescheduleRequest = loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequest,
requestJSON);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(2400.00, true, "01 January 2023"), //
installment(1411.37, false, "01 February 2023"), //
installment(1481.94, false, "01 March 2023"), //
installment(1556.04, false, "01 April 2023"), //
installment(1633.84, false, "01 May 2023"), //
installment(815.94, false, "01 June 2023"), //
installment(856.74, false, "01 July 2023"), //
installment(899.57, false, "01 August 2023"), //
installment(944.56, false, "01 September 2023") //
);
});
}
@Test
public void testRescheduleAddExtraInstallmentsMultipleDisbursementWithDownPaymentWithInterest() {
runAt("01 January 2023", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
// Create Loan Product
Long loanProductId = createLoanProductWith20PctDownPaymentWithDecliningBalanceInterest(true, true, 5.0);
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, loanProductId, "01 January 2023", 15000.0, 6, postLoansRequest -> {
postLoansRequest.interestRatePerPeriod(BigDecimal.valueOf(5));
postLoansRequest.interestRatePerPeriod(BigDecimal.valueOf(5));
postLoansRequest.repaymentEvery(1);
postLoansRequest.loanTermFrequencyType(RepaymentFrequencyType.MONTHS);
postLoansRequest.repaymentFrequencyType(RepaymentFrequencyType.MONTHS);
postLoansRequest.loanTermFrequency(6);
});
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(3000.00, false, "01 January 2023"), //
installment(1764.21, false, "01 February 2023"), //
installment(1852.42, false, "01 March 2023"), //
installment(1945.04, false, "01 April 2023"), //
installment(2042.29, false, "01 May 2023"), //
installment(2144.41, false, "01 June 2023"), //
installment(2251.63, false, "01 July 2023") //
);
// 1st Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(12000.00), "01 January 2023");
// verify transactions
verifyTransactions(loanId, //
transaction(2400.0, "Down Payment", "01 January 2023"), //
transaction(12000.0, "Disbursement", "01 January 2023") //
);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(2400.00, true, "01 January 2023"), //
installment(1411.37, false, "01 February 2023"), //
installment(1481.94, false, "01 March 2023"), //
installment(1556.04, false, "01 April 2023"), //
installment(1633.84, false, "01 May 2023"), //
installment(1715.53, false, "01 June 2023"), //
installment(1801.28, false, "01 July 2023") //
);
updateBusinessDate("02 May 2023");
// 2nd Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(3000.00), "02 May 2023");
// verify transactions
verifyTransactions(loanId, //
transaction(2400.0, "Down Payment", "01 January 2023"), //
transaction(12000.0, "Disbursement", "01 January 2023"), //
transaction(3000.00, "Disbursement", "02 May 2023"), //
transaction(600.0, "Down Payment", "02 May 2023") //
);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(2400.00, true, "01 January 2023"), //
installment(1884.21, false, "01 February 2023"), //
installment(1978.42, false, "01 March 2023"), //
installment(2077.34, false, "01 April 2023"), //
installment(2181.21, false, "01 May 2023"), //
installment(3000.0, null, "02 May 2023"), //
installment(600.0, false, "02 May 2023"), //
installment(2174.14, false, "01 June 2023"), //
installment(1704.68, false, "01 July 2023") //
);
updateBusinessDate("01 June 2023");
String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnInterest(null).updateGraceOnPrincipal(null)
.updateExtraTerms("2").updateNewInterestRate(null).updateRescheduleFromDate("01 June 2023").updateAdjustedDueDate(null)
.updateSubmittedOnDate("01 June 2023").updateRescheduleReasonId("1").build(loanId.toString());
Integer loanRescheduleRequest = loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
requestJSON = new LoanRescheduleRequestTestBuilder().updateSubmittedOnDate("01 June 2023")
.getApproveLoanRescheduleRequestJSON();
Integer approveLoanRescheduleRequest = loanRescheduleRequestHelper.approveLoanRescheduleRequest(loanRescheduleRequest,
requestJSON);
// Verify Repayment Schedule
verifyRepaymentSchedule(loanId, //
installment(12000.0, null, "01 January 2023"), //
installment(2400.00, true, "01 January 2023"), //
installment(1884.21, false, "01 February 2023"), //
installment(1978.42, false, "01 March 2023"), //
installment(2077.34, false, "01 April 2023"), //
installment(2181.21, false, "01 May 2023"), //
installment(3000.0, null, "02 May 2023"), //
installment(600.0, false, "02 May 2023"), //
installment(903.80, false, "01 June 2023"), //
installment(945.12, false, "01 July 2023"), //
installment(992.37, false, "01 August 2023"), //
installment(1037.53, false, "01 September 2023")//
);
});
}
private Long createLoanProductWith25PctDownPayment(boolean autoDownPaymentEnabled, boolean multiDisburseEnabled) {
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
product.setMultiDisburseLoan(multiDisburseEnabled);
@@ -117,7 +455,7 @@ public class LoanRescheduleTestWithDownpayment extends BaseLoanIntegrationTest {
}
product.setEnableDownPayment(true);
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE);
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE_25);
product.setEnableAutoRepaymentForDownPayment(autoDownPaymentEnabled);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
@@ -128,7 +466,76 @@ public class LoanRescheduleTestWithDownpayment extends BaseLoanIntegrationTest {
assertEquals(TRUE, getLoanProductsProductIdResponse.getEnableDownPayment());
assertNotNull(getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment());
assertEquals(0, getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE));
assertEquals(0,
getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE_25));
assertEquals(autoDownPaymentEnabled, getLoanProductsProductIdResponse.getEnableAutoRepaymentForDownPayment());
assertEquals(multiDisburseEnabled, getLoanProductsProductIdResponse.getMultiDisburseLoan());
return loanProductId;
}
private Long createLoanProductWith33PctDownPayment(boolean autoDownPaymentEnabled, boolean multiDisburseEnabled) {
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
product.setMultiDisburseLoan(multiDisburseEnabled);
product.repaymentEvery(1);
product.repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue());
product.installmentAmountInMultiplesOf(null);
if (!multiDisburseEnabled) {
product.disallowExpectedDisbursements(null);
product.setAllowApprovedDisbursedAmountsOverApplied(null);
product.overAppliedCalculationType(null);
product.overAppliedNumber(null);
}
product.setEnableDownPayment(true);
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE_33);
product.setEnableAutoRepaymentForDownPayment(autoDownPaymentEnabled);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductResponse.getResourceId());
Long loanProductId = loanProductResponse.getResourceId();
assertEquals(TRUE, getLoanProductsProductIdResponse.getEnableDownPayment());
assertNotNull(getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment());
assertEquals(0,
getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE_33));
assertEquals(autoDownPaymentEnabled, getLoanProductsProductIdResponse.getEnableAutoRepaymentForDownPayment());
assertEquals(multiDisburseEnabled, getLoanProductsProductIdResponse.getMultiDisburseLoan());
return loanProductId;
}
private Long createLoanProductWith20PctDownPaymentWithDecliningBalanceInterest(boolean autoDownPaymentEnabled,
boolean multiDisburseEnabled, double interestRate) {
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
product.setMultiDisburseLoan(multiDisburseEnabled);
product.repaymentEvery(1);
product.repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue());
product.installmentAmountInMultiplesOf(null);
product.interestRatePerPeriod(interestRate);
if (!multiDisburseEnabled) {
product.disallowExpectedDisbursements(null);
product.setAllowApprovedDisbursedAmountsOverApplied(null);
product.overAppliedCalculationType(null);
product.overAppliedNumber(null);
}
product.setEnableDownPayment(true);
product.setDisbursedAmountPercentageForDownPayment(DOWN_PAYMENT_PERCENTAGE_20);
product.setEnableAutoRepaymentForDownPayment(autoDownPaymentEnabled);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductResponse.getResourceId());
Long loanProductId = loanProductResponse.getResourceId();
assertEquals(TRUE, getLoanProductsProductIdResponse.getEnableDownPayment());
assertNotNull(getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment());
assertEquals(0,
getLoanProductsProductIdResponse.getDisbursedAmountPercentageForDownPayment().compareTo(DOWN_PAYMENT_PERCENTAGE_20));
assertEquals(autoDownPaymentEnabled, getLoanProductsProductIdResponse.getEnableAutoRepaymentForDownPayment());
assertEquals(multiDisburseEnabled, getLoanProductsProductIdResponse.getMultiDisburseLoan());
return loanProductId;
@@ -446,8 +446,8 @@ public class LoanSpecificDueDateChargeAfterMaturityTest {
.currencyCode("USD").name(Utils.randomStringGenerator("PENALTY_" + Calendar.getInstance().getTimeInMillis(), 5))
.chargeAppliesTo(1).locale("en").active(true));
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId, 1000L, 30, 30, 1, 0,
"01 September 2023", "01 September 2023");
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId, 1000L, 30, 30, 1,
BigDecimal.ZERO, "01 September 2023", "01 September 2023");
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
@@ -548,7 +548,7 @@ public class LoanSpecificDueDateChargeAfterMaturityTest {
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final Long principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final int interestRate,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate) {
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
return loanTransactionHelper.applyLoan(new PostLoansRequest().clientId(clientId).productId(loanProductId.longValue())
@@ -80,7 +80,7 @@ public class LoanWaiveChargeTest extends BaseLoanIntegrationTest {
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest);
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
approveLoanRequest(amount));
approveLoanRequest(amount, "01 January 2023"));
Long loanId = approvedLoanResult.getLoanId();
appliedLoanId.set(loanId);
@@ -104,8 +104,8 @@ public class RefundForActiveLoansWithAdvancedPaymentAllocationTest {
Integer loanProductId = createLoanProduct("1000", "30", "4", LoanScheduleProcessingType.VERTICAL, assetAccount, incomeAccount,
expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), loanProductId, 1000L, 90, 30, 3, 0,
"01 January 2023", "01 January 2023");
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), loanProductId, 1000L, 90, 30, 3,
BigDecimal.ZERO, "01 January 2023", "01 January 2023");
int loanId = loanResponse.getLoanId().intValue();
@@ -303,8 +303,8 @@ public class RefundForActiveLoansWithAdvancedPaymentAllocationTest {
Integer loanProductId = createLoanProduct("1000", "30", "4", LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount,
expenseAccount, overpaymentAccount);
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), loanProductId, 1000L, 90, 30, 3, 0,
"01 January 2023", "01 January 2023");
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), loanProductId, 1000L, 90, 30, 3,
BigDecimal.ZERO, "01 January 2023", "01 January 2023");
int loanId = loanResponse.getLoanId().intValue();
@@ -529,7 +529,7 @@ public class RefundForActiveLoansWithAdvancedPaymentAllocationTest {
}
private static PostLoansResponse applyForLoanApplication(final Long clientId, final Integer loanProductId, final Long principal,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final int interestRate,
final int loanTermFrequency, final int repaymentAfterEvery, final int numberOfRepayments, final BigDecimal interestRate,
final String expectedDisbursementDate, final String submittedOnDate) {
log.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
return loanTransactionHelper.applyLoan(new PostLoansRequest().clientId(clientId).productId(loanProductId.longValue())