FINERACT-1981: Order disbursements and repayment periods properly
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DisbursementData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DisbursementData.java
index 243426d..53e318f 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DisbursementData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/DisbursementData.java
@@ -23,6 +23,7 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
/**
* Immutable data object representing disbursement information.
@@ -89,9 +90,12 @@
return DateUtils.compare(obj.expectedDisbursementDate, this.expectedDisbursementDate);
}
- public boolean isDueForDisbursement(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
+ public boolean isDueForDisbursement(LoanScheduleType loanScheduleType, final LocalDate fromDate, final LocalDate toDate) {
final LocalDate dueDate = disbursementDate();
- return occursOnDayFromAndUpToAndIncluding(fromNotInclusive, upToAndInclusive, dueDate);
+ return switch (loanScheduleType) {
+ case CUMULATIVE -> occursOnDayFromAndUpToAndIncluding(fromDate, toDate, dueDate);
+ case PROGRESSIVE -> occursOnDayFromAndIncludingAndUpTo(fromDate, toDate, dueDate);
+ };
}
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
@@ -99,6 +103,12 @@
return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
}
+ private boolean occursOnDayFromAndIncludingAndUpTo(final LocalDate fromInclusive, final LocalDate upToNotInclusive,
+ final LocalDate target) {
+ return (DateUtils.isEqual(target, fromInclusive) || DateUtils.isAfter(target, fromInclusive))
+ && DateUtils.isBefore(target, upToNotInclusive);
+ }
+
public BigDecimal getWaivedChargeAmount() {
return this.waivedChargeAmount == null ? BigDecimal.ZERO : this.waivedChargeAmount;
}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index 9994201..fefbd02 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -282,7 +282,7 @@
List<LoanRepaymentScheduleInstallment> installments) {
final MathContext mc = MoneyHelper.getMathContext();
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
- i -> !i.getDueDate().isBefore(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
+ i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
.toList();
int noCandidateRepaymentInstallments = candidateRepaymentInstallments.size();
LoanProductRelatedDetail loanProductRelatedDetail = disbursementTransaction.getLoan().getLoanRepaymentScheduleDetail();
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleType.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleType.java
index f561733..d9a7046 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleType.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleType.java
@@ -39,4 +39,11 @@
public EnumOptionData asEnumOptionData() {
return new EnumOptionData((long) this.ordinal(), this.name(), this.humanReadableName);
}
+
+ public static LoanScheduleType fromEnumOptionData(EnumOptionData enumOptionData) {
+ if (enumOptionData == null) {
+ return null;
+ }
+ return valueOf(enumOptionData.getCode());
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index e379e84..7f6da6f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -47,7 +47,6 @@
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;
import java.io.InputStream;
-import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -953,7 +952,7 @@
loanBasicDetails.getFeeChargesAtDisbursementCharged());
repaymentSchedule = this.loanReadPlatformService.retrieveRepaymentSchedule(resolvedLoanId, repaymentScheduleRelatedData,
disbursementData, loanBasicDetails.isInterestRecalculationEnabled(),
- loanBasicDetails.getSummary() != null ? loanBasicDetails.getSummary().getFeeChargesPaid() : BigDecimal.ZERO);
+ LoanScheduleType.fromEnumOptionData(loanBasicDetails.getLoanScheduleType()));
if (associationParameters.contains(DataTableApiConstant.futureScheduleAssociateParamName)
&& loanBasicDetails.isInterestRecalculationEnabled()) {
@@ -965,8 +964,9 @@
&& loanBasicDetails.isInterestRecalculationEnabled()
&& LoanStatus.fromInt(loanBasicDetails.getStatus().getId().intValue()).isActive()) {
mandatoryResponseParameters.add(DataTableApiConstant.originalScheduleAssociateParamName);
- LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService
- .retrieveRepaymentArchiveSchedule(resolvedLoanId, repaymentScheduleRelatedData, disbursementData);
+ LoanScheduleData loanScheduleData = this.loanScheduleHistoryReadPlatformService.retrieveRepaymentArchiveSchedule(
+ resolvedLoanId, repaymentScheduleRelatedData, disbursementData,
+ LoanScheduleType.fromEnumOptionData(loanBasicDetails.getLoanScheduleType()));
loanBasicDetails = LoanAccountData.withOriginalSchedule(loanBasicDetails, loanScheduleData);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
index d0c1ba0..2399093 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java
@@ -28,7 +28,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.TreeMap;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
@@ -85,26 +84,6 @@
boolean isFirstRepayment = true;
- if (loanApplicationTerms.isMultiDisburseLoan()) {
- /* fetches the first tranche amount and also updates other tranche details to map */
- Money disburseAmt = Money.of(currency, getDisbursementAmount(loanApplicationTerms, scheduleParams.getPeriodStartDate(),
- scheduleParams.getDisburseDetailMap(), scheduleParams.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);
- scheduleParams.setPrincipalToBeScheduled(remainingPrincipalAmt);
- loanApplicationTerms.setPrincipal(remainingPrincipalAmt);
- scheduleParams.setOutstandingBalance(remainingPrincipalAmt);
- scheduleParams.setOutstandingBalanceAsPerRest(remainingPrincipalAmt);
- loanApplicationTerms.resetFixedEmiAmount();
- }
-
// charges which depends on total loan interest will be added to this
// set and handled separately after all installments generated
final Set<LoanCharge> nonCompoundingCharges = separateTotalCompoundingPercentageCharges(loanCharges);
@@ -134,13 +113,8 @@
}
// 5 determine principal,interest of repayment period
- PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
- getPaymentPeriodsInOneYearCalculator(), currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
- scheduleParams.getTotalCumulativePrincipal().minus(scheduleParams.getReducePrincipal()),
- scheduleParams.getTotalCumulativeInterest(), loanApplicationTerms.getTotalInterestDue(),
- scheduleParams.getTotalOutstandingInterestPaymentDueToGrace(), scheduleParams.getOutstandingBalanceAsPerRest(),
- loanApplicationTerms, scheduleParams.getPeriodNumber(), mc, null, scheduleParams.getCompoundingMap(), null,
- scheduledDueDate, null);
+ PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(loanApplicationTerms,
+ scheduleParams, mc);
// update cumulative fields for principal
currentPeriodParams.setPrincipalForThisPeriod(principalInterestForThisPeriod.principal());
@@ -241,12 +215,8 @@
public abstract PaymentPeriodsInOneYearCalculator getPaymentPeriodsInOneYearCalculator();
- public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
- BigDecimal interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
- Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, Money outstandingBalance,
- LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc, TreeMap<LocalDate, Money> principalVariation,
- Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate,
- Collection<LoanTermVariationsData> termVariations);
+ public abstract PrincipalInterest calculatePrincipalInterestComponentsForPeriod(LoanApplicationTerms loanApplicationTerms,
+ LoanScheduleParams loanScheduleParams, MathContext mc);
// Private, internal methods
private BigDecimal deriveTotalChargesDueAtTimeOfDisbursement(final Set<LoanCharge> loanCharges) {
@@ -266,7 +236,8 @@
Money principalToBeScheduled;
if (loanApplicationTerms.isMultiDisburseLoan()) {
if (loanApplicationTerms.getTotalDisbursedAmount().isGreaterThanZero()) {
- principalToBeScheduled = loanApplicationTerms.getTotalMultiDisbursedAmount();
+ principalToBeScheduled = Money.of(loanApplicationTerms.getCurrency(),
+ loanApplicationTerms.getDisbursementDatas().get(0).getPrincipal());
} else if (loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) {
principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal();
} else {
@@ -300,6 +271,9 @@
loanScheduleParams, loanApplicationTerms.getExpectedDisbursementDate(), disbursementData.getPrincipal());
periods.add(downPaymentPeriod);
}
+ } else {
+ loanScheduleParams.getDisburseDetailMap().put(disbursementData.disbursementDate(),
+ Money.of(loanApplicationTerms.getCurrency(), disbursementData.getPrincipal()));
}
}
@@ -313,14 +287,19 @@
if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount, loanApplicationTerms.getInstallmentAmountInMultiplesOf());
}
- LoanScheduleModelDownPaymentPeriod installment = LoanScheduleModelDownPaymentPeriod
- .downPayment(scheduleParams.getInstalmentNumber(), date, downPaymentAmount, scheduleParams.getOutstandingBalance());
+ LoanScheduleModelDownPaymentPeriod installment = LoanScheduleModelDownPaymentPeriod.downPayment(
+ scheduleParams.getInstalmentNumber(), date, downPaymentAmount,
+ scheduleParams.getOutstandingBalance().minus(downPaymentAmount));
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
scheduleParams.incrementInstalmentNumber();
scheduleParams.addTotalRepaymentExpected(downPaymentAmount);
-
+ scheduleParams.reduceOutstandingBalance(downPaymentAmount);
+ scheduleParams.reduceOutstandingBalanceAsPerRest(downPaymentAmount);
+ scheduleParams.setPrincipalToBeScheduled(scheduleParams.getPrincipalToBeScheduled().minus(downPaymentAmount));
+ loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().minus(downPaymentAmount));
+ loanApplicationTerms.resetFixedEmiAmount();
return installment;
}
@@ -345,8 +324,9 @@
private void processDisbursements(final LoanApplicationTerms loanApplicationTerms, final BigDecimal chargesDueAtTimeOfDisbursement,
LoanScheduleParams scheduleParams, final Collection<LoanScheduleModelPeriod> periods, final LocalDate scheduledDueDate) {
for (Map.Entry<LocalDate, Money> disburseDetail : scheduleParams.getDisburseDetailMap().entrySet()) {
- if (disburseDetail.getKey().isAfter(scheduleParams.getPeriodStartDate())
- && !disburseDetail.getKey().isAfter(scheduledDueDate)) {
+ if ((disburseDetail.getKey().isEqual(scheduleParams.getPeriodStartDate())
+ || disburseDetail.getKey().isAfter(scheduleParams.getPeriodStartDate()))
+ && disburseDetail.getKey().isBefore(scheduledDueDate)) {
// validation check for amount not exceeds specified max
// amount as per the configuration
loanApplicationTerms.getMaxOutstandingBalance();
@@ -378,11 +358,10 @@
}
// updates actual outstanding balance with new
// disbursement detail
- Money remainingPrincipal = disburseDetail.getValue().minus(downPaymentAmt);
- scheduleParams.addOutstandingBalance(remainingPrincipal);
- scheduleParams.addOutstandingBalanceAsPerRest(remainingPrincipal);
- scheduleParams.addPrincipalToBeScheduled(remainingPrincipal);
- loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(remainingPrincipal));
+ scheduleParams.addOutstandingBalance(disburseDetail.getValue());
+ scheduleParams.addOutstandingBalanceAsPerRest(disburseDetail.getValue());
+ scheduleParams.addPrincipalToBeScheduled(disburseDetail.getValue());
+ loanApplicationTerms.setPrincipal(loanApplicationTerms.getPrincipal().plus(disburseDetail.getValue()));
loanApplicationTerms.resetFixedEmiAmount();
}
}
@@ -585,43 +564,6 @@
return interestCharges;
}
- private BigDecimal getDisbursementAmount(final LoanApplicationTerms loanApplicationTerms, LocalDate disbursementDate,
- final Map<LocalDate, Money> disburseDetails, final boolean excludePastUnDisbursed) {
-
- // this method relates to multi-disbursement loans
- BigDecimal principal = BigDecimal.ZERO;
- if (loanApplicationTerms.getDisbursementDatas().size() == 0) {
- // non tranche loans have no disbursement data entries in submitted and approved status
- // the appropriate approved amount or applied for amount is used to show a proposed schedule
- if (loanApplicationTerms.getApprovedPrincipal().getAmount().compareTo(BigDecimal.ZERO) > 0) {
- principal = loanApplicationTerms.getApprovedPrincipal().getAmount();
- } else {
- principal = loanApplicationTerms.getPrincipal().getAmount();
- }
- } else {
- MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency();
- for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) {
- if (disbursementData.disbursementDate().equals(disbursementDate)) {
- principal = principal.add(disbursementData.getPrincipal());
- } else if (!excludePastUnDisbursed || disbursementData.isDisbursed()
- || !disbursementData.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) {
- /*
- * JW: sums up amounts by disbursal date in case of side-effect issues. Original assumed that there
- * were no duplicate disbursal dates and 'put' each amount into the map keyed by date
- */
- Money previousSum = disburseDetails.get(disbursementData.disbursementDate());
- BigDecimal sumToNow = BigDecimal.ZERO;
- if (previousSum != null) {
- sumToNow = previousSum.getAmount();
- }
- sumToNow = sumToNow.add(disbursementData.getPrincipal());
- disburseDetails.put(disbursementData.disbursementDate(), Money.of(currency, sumToNow));
- }
- }
- }
- return principal;
- }
-
private void updateOutstandingBalance(LoanScheduleParams scheduleParams, ScheduleCurrentPeriodParams currentPeriodParams) {
// update outstandingLoanBlance using current period
// 'principalDue'
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
index 99a106e..f05675e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java
@@ -20,13 +20,9 @@
import java.math.BigDecimal;
import java.math.MathContext;
-import java.time.LocalDate;
-import java.util.Collection;
-import java.util.Map;
-import java.util.TreeMap;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.organisation.monetary.domain.Money;
-import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams;
import org.springframework.stereotype.Component;
@Component
@@ -47,21 +43,30 @@
}
@Override
- public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(PaymentPeriodsInOneYearCalculator calculator,
- BigDecimal interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
- Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, Money outstandingBalance,
- LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc, TreeMap<LocalDate, Money> principalVariation,
- Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate,
- Collection<LoanTermVariationsData> termVariations) {
+ public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(LoanApplicationTerms loanApplicationTerms,
+ LoanScheduleParams loanScheduleParams, MathContext mc) {
// TODO: handle interest calculation
- Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator, outstandingBalance,
- periodNumber, mc, Money.zero(loanApplicationTerms.getCurrency()));
- final Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment);
+ int periodNumber = loanScheduleParams.getPeriodNumber();
+ BigDecimal fixedEMIAmount = loanApplicationTerms.getFixedEmiAmount();
+ Money calculatedPrincipal;
+ if (fixedEMIAmount == null) {
+ int noRemainingPeriod = loanApplicationTerms.getActualNoOfRepaymnets() - periodNumber + 1;
+ calculatedPrincipal = loanScheduleParams.getOutstandingBalance().dividedBy(noRemainingPeriod, mc.getRoundingMode());
+ if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
+ calculatedPrincipal = Money.roundToMultiplesOf(calculatedPrincipal,
+ loanApplicationTerms.getInstallmentAmountInMultiplesOf());
+ }
+ loanApplicationTerms.setFixedEmiAmount(calculatedPrincipal.getAmount());
+ } else {
+ calculatedPrincipal = Money.of(loanApplicationTerms.getCurrency(), fixedEMIAmount);
+ }
// adjust if needed
- principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
- totalCumulativePrincipalToDate, periodNumber);
+ if (periodNumber == loanApplicationTerms.getActualNoOfRepaymnets()) {
+ Money remainingAmount = loanScheduleParams.getOutstandingBalance().minus(calculatedPrincipal);
+ calculatedPrincipal = calculatedPrincipal.plus(remainingAmount);
+ }
- return new PrincipalInterest(principalForThisInstallment, Money.zero(loanApplicationTerms.getCurrency()),
+ return new PrincipalInterest(calculatedPrincipal, Money.zero(loanApplicationTerms.getCurrency()),
Money.zero(loanApplicationTerms.getCurrency()));
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformService.java
index 22270a1..3b25c50 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformService.java
@@ -23,13 +23,14 @@
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
public interface LoanScheduleHistoryReadPlatformService {
Integer fetchCurrentVersionNumber(Long loanId);
LoanScheduleData retrieveRepaymentArchiveSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
- Collection<DisbursementData> disbursementData);
+ Collection<DisbursementData> disbursementData, LoanScheduleType loanScheduleType);
Map<String, Object> fetchOldAuditDates(Long id);
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java
index d098c63..8b35fef 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleHistoryReadPlatformServiceImpl.java
@@ -37,6 +37,7 @@
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
@@ -68,7 +69,8 @@
@Override
public LoanScheduleData retrieveRepaymentArchiveSchedule(final Long loanId,
- final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData) {
+ final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
+ LoanScheduleType loanScheduleType) {
try {
this.context.authenticatedUser();
@@ -77,7 +79,7 @@
return null;
}
final LoanScheduleArchiveResultSetExtractor fullResultsetExtractor = new LoanScheduleArchiveResultSetExtractor(
- repaymentScheduleRelatedLoanData, disbursementData);
+ repaymentScheduleRelatedLoanData, disbursementData, loanScheduleType);
final String sql = "select " + fullResultsetExtractor.schema()
+ " where ls.loan_id = ? and ls.version = ? order by ls.loan_id, ls.installment";
@@ -99,17 +101,19 @@
private final DisbursementData disbursement;
private final BigDecimal totalFeeChargesDueAtDisbursement;
private final Collection<DisbursementData> disbursementData;
+ private final LoanScheduleType loanScheduleType;
private LocalDate lastDueDate;
private BigDecimal outstandingLoanPrincipalBalance;
LoanScheduleArchiveResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
- Collection<DisbursementData> disbursementData) {
+ Collection<DisbursementData> disbursementData, LoanScheduleType loanScheduleType) {
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
this.lastDueDate = this.disbursement.disbursementDate();
this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
this.disbursementData = disbursementData;
+ this.loanScheduleType = loanScheduleType;
}
public String schema() {
@@ -163,7 +167,7 @@
data.getPrincipal(), this.totalFeeChargesDueAtDisbursement, data.isDisbursed());
periods.add(periodData);
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
- } else if (data.isDueForDisbursement(fromDate, dueDate)
+ } else if (data.isDueForDisbursement(loanScheduleType, fromDate, dueDate)
&& this.outstandingLoanPrincipalBalance.compareTo(BigDecimal.ZERO) > 0) {
principal = principal.add(data.getPrincipal());
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(),
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
index 235b6b8..1b0c64e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -18,7 +18,6 @@
*/
package org.apache.fineract.portfolio.loanaccount.service;
-import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import java.util.List;
@@ -43,6 +42,7 @@
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
+import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
public interface LoanReadPlatformService {
@@ -51,7 +51,7 @@
LoanAccountData fetchRepaymentScheduleData(LoanAccountData accountData);
LoanScheduleData retrieveRepaymentSchedule(Long loanId, RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData,
- Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges);
+ Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType);
Collection<LoanTransactionData> retrieveLoanTransactions(Long loanId);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index f0d0240..6adaa99 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -245,7 +245,7 @@
final Collection<DisbursementData> disbursementData = retrieveLoanDisbursementDetails(accountData.getId());
final LoanScheduleData repaymentSchedule = retrieveRepaymentSchedule(accountData.getId(), repaymentScheduleRelatedData,
disbursementData, accountData.isInterestRecalculationEnabled(),
- accountData.getSummary() != null ? accountData.getSummary().getFeeChargesPaid() : BigDecimal.ZERO);
+ LoanScheduleType.fromEnumOptionData(accountData.getLoanScheduleType()));
accountData.setRepaymentSchedule(repaymentSchedule);
return accountData;
}
@@ -253,13 +253,13 @@
@Override
public LoanScheduleData retrieveRepaymentSchedule(final Long loanId,
final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData, Collection<DisbursementData> disbursementData,
- boolean isInterestRecalculationEnabled, BigDecimal totalPaidFeeCharges) {
+ boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType) {
try {
this.context.authenticatedUser();
final LoanScheduleResultSetExtractor fullResultsetExtractor = new LoanScheduleResultSetExtractor(
- repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled);
+ repaymentScheduleRelatedLoanData, disbursementData, isInterestRecalculationEnabled, loanScheduleType);
final String sql = "select " + fullResultsetExtractor.schema() + " where ls.loan_id = ? order by ls.loan_id, ls.installment";
return this.jdbcTemplate.query(sql, fullResultsetExtractor, loanId); // NOSONAR
@@ -1123,19 +1123,21 @@
private final DisbursementData disbursement;
private final BigDecimal totalFeeChargesDueAtDisbursement;
private final Collection<DisbursementData> disbursementData;
+ private final LoanScheduleType loanScheduleType;
private LocalDate lastDueDate;
private BigDecimal outstandingLoanPrincipalBalance;
- private boolean excludePastUndisbursed;
+ private boolean excludePastUnDisbursed;
LoanScheduleResultSetExtractor(final RepaymentScheduleRelatedLoanData repaymentScheduleRelatedLoanData,
- Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled) {
+ Collection<DisbursementData> disbursementData, boolean isInterestRecalculationEnabled, LoanScheduleType loanScheduleType) {
this.currency = repaymentScheduleRelatedLoanData.getCurrency();
this.disbursement = repaymentScheduleRelatedLoanData.disbursementData();
this.totalFeeChargesDueAtDisbursement = repaymentScheduleRelatedLoanData.getTotalFeeChargesAtDisbursement();
this.lastDueDate = this.disbursement.disbursementDate();
this.outstandingLoanPrincipalBalance = this.disbursement.getPrincipal();
this.disbursementData = disbursementData;
- this.excludePastUndisbursed = isInterestRecalculationEnabled;
+ this.excludePastUnDisbursed = isInterestRecalculationEnabled;
+ this.loanScheduleType = loanScheduleType;
}
public String schema() {
@@ -1160,7 +1162,7 @@
this.disbursement.disbursementDate(), this.disbursement.getPrincipal(), this.totalFeeChargesDueAtDisbursement,
this.disbursement.isDisbursed());
- final Collection<LoanSchedulePeriodData> periods = new ArrayList<>();
+ final List<LoanSchedulePeriodData> periods = new ArrayList<>();
final MonetaryCurrency monCurrency = new MonetaryCurrency(this.currency.getCode(), this.currency.getDecimalPlaces(),
this.currency.getInMultiplesOf());
BigDecimal totalPrincipalDisbursed = BigDecimal.ZERO;
@@ -1170,7 +1172,7 @@
totalPrincipalDisbursed = Money.of(monCurrency, this.disbursement.getPrincipal()).getAmount();
} else {
if (!this.disbursement.isDisbursed()) {
- excludePastUndisbursed = false;
+ excludePastUnDisbursed = false;
}
for (DisbursementData data : disbursementData) {
if (data.getChargeAmount() != null) {
@@ -1211,56 +1213,11 @@
final LocalDate obligationsMetOnDate = JdbcSupport.getLocalDate(rs, "obligationsMetOnDate");
final boolean complete = rs.getBoolean("complete");
final boolean isAdditional = rs.getBoolean("isAdditional");
- BigDecimal principal = BigDecimal.ZERO;
+ BigDecimal disbursedAmount = BigDecimal.ZERO;
if (!isAdditional) {
- for (final DisbursementData data : disbursementData) {
- if (fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate)
- && !disbursementPeriodIds.contains(data.getId())) {
- principal = principal.add(data.getPrincipal());
- LoanSchedulePeriodData periodData = null;
- if (data.getChargeAmount() == null) {
- periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
- disbursementChargeAmount, data.isDisbursed());
- } else {
- periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
- disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount),
- data.isDisbursed());
- }
- periods.add(periodData);
- this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
- disbursementPeriodIds.add(data.getId());
- } else if (data.isDueForDisbursement(fromDate, dueDate) && !disbursementPeriodIds.contains(data.getId())) {
- if (!excludePastUndisbursed || data.isDisbursed() || !DateUtils.isBeforeBusinessDate(data.disbursementDate())) {
- principal = principal.add(data.getPrincipal());
- LoanSchedulePeriodData periodData;
- if (data.getChargeAmount() == null) {
- periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
- BigDecimal.ZERO, data.isDisbursed());
- } else {
- periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
- data.getChargeAmount(), data.isDisbursed());
- }
- periods.add(periodData);
- this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
- disbursementPeriodIds.add(data.getId());
- }
- } else if (fromDate.equals(dueDate) && data.disbursementDate().equals(fromDate)
- && !disbursementPeriodIds.contains(data.getId())) {
- principal = principal.add(data.getPrincipal());
- LoanSchedulePeriodData periodData = null;
- if (data.getChargeAmount() == null) {
- periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
- disbursementChargeAmount, data.isDisbursed());
- } else {
- periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(),
- disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount),
- data.isDisbursed());
- }
- periods.add(periodData);
- this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
- disbursementPeriodIds.add(data.getId());
- }
- }
+ disbursedAmount = processDisbursementData(loanScheduleType, disbursementData, fromDate, dueDate, disbursementPeriodIds,
+ disbursementChargeAmount, waivedChargeAmount, periods);
+
}
// Add the Charge back or Credits to the initial amount to avoid negative balance
final BigDecimal credits = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalCredits");
@@ -1268,7 +1225,7 @@
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(credits);
}
- totalPrincipalDisbursed = totalPrincipalDisbursed.add(principal);
+ totalPrincipalDisbursed = totalPrincipalDisbursed.add(disbursedAmount);
Integer daysInPeriod = 0;
if (fromDate != null) {
@@ -1379,6 +1336,39 @@
totalPaidLate.getAmount(), totalOutstanding.getAmount(), totalCredits.getAmount());
}
+ private BigDecimal processDisbursementData(LoanScheduleType loanScheduleType, Collection<DisbursementData> disbursementData,
+ LocalDate fromDate, LocalDate dueDate, Set<Long> disbursementPeriodIds, BigDecimal disbursementChargeAmount,
+ BigDecimal waivedChargeAmount, List<LoanSchedulePeriodData> periods) {
+ BigDecimal disbursedAmount = BigDecimal.ZERO;
+ for (final DisbursementData data : disbursementData) {
+ boolean isDueForDisbursement = data.isDueForDisbursement(loanScheduleType, fromDate, dueDate);
+ if (((fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate))
+ || (fromDate.equals(dueDate) && data.disbursementDate().equals(fromDate))
+ || canAddDisbursementData(data, isDueForDisbursement, excludePastUnDisbursed))
+ && !disbursementPeriodIds.contains(data.getId())) {
+ disbursedAmount = disbursedAmount.add(data.getPrincipal());
+ LoanSchedulePeriodData periodData = createLoanSchedulePeriodData(data, disbursementChargeAmount, waivedChargeAmount);
+ periods.add(periodData);
+ this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(periodData.getPrincipalDisbursed());
+ disbursementPeriodIds.add(data.getId());
+ }
+ }
+ return disbursedAmount;
+ }
+
+ private LoanSchedulePeriodData createLoanSchedulePeriodData(final DisbursementData data, BigDecimal disbursementChargeAmount,
+ BigDecimal waivedChargeAmount) {
+ BigDecimal chargeAmount = data.getChargeAmount() == null ? disbursementChargeAmount
+ : disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount);
+ return LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), data.getPrincipal(), chargeAmount,
+ data.isDisbursed());
+ }
+
+ private boolean canAddDisbursementData(DisbursementData data, boolean isDueForDisbursement, boolean excludePastUnDisbursed) {
+ return (!excludePastUnDisbursed || data.isDisbursed() || !DateUtils.isBeforeBusinessDate(data.disbursementDate()))
+ && isDueForDisbursement;
+ }
+
}
private static final class LoanTransactionsMapper implements RowMapper<LoanTransactionData> {
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
index 64c51a6..e587e12 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
@@ -57,7 +57,6 @@
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CommonConstants;
-import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
import org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.accounting.Account;
@@ -78,7 +77,7 @@
import org.slf4j.LoggerFactory;
@ExtendWith(LoanTestLifecycleExtension.class)
-public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
+public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoanIntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(AdvancedPaymentAllocationLoanRepaymentScheduleTest.class);
private static final String DATETIME_PATTERN = "dd MMMM yyyy";
@@ -122,10 +121,7 @@
@Test
public void uc1() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -182,9 +178,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 4, 125.0, 125.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC2: Overpayment1
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
@@ -195,10 +189,7 @@
@Test
public void uc2() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -255,9 +246,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 25.0, 0.0);
validateLoanTransaction(loanDetails, 4, 125.0, 100.0, 25.0, 0.0);
assertTrue(loanDetails.getStatus().getOverpaid());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC3: Overpayment2
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
@@ -267,10 +256,7 @@
@Test
public void uc3() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -327,9 +313,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 25.0, 0.0);
validateLoanTransaction(loanDetails, 4, 125.0, 100.0, 25.0, 0.0);
assertTrue(loanDetails.getStatus().getOverpaid());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC4: Delinquent balance
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
@@ -340,10 +324,7 @@
@Test
public void uc4() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -419,9 +400,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 5, 360.0, 360.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC5: Refund past due
@@ -432,10 +411,7 @@
// 4. Pay rest on time
@Test
public void uc5() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -507,9 +483,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 58.34, 0.0);
validateLoanTransaction(loanDetails, 5, 66.66, 66.66, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC7: Refund & due reamortization
@@ -521,10 +495,7 @@
// 4. Pay rest on time
@Test
public void uc7() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -594,9 +565,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 100.0, 0.0);
validateLoanTransaction(loanDetails, 5, 25.0, 25.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC8: Refund after due & past due
@@ -608,10 +577,7 @@
// 4. Pay rest on time
@Test
public void uc8() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -686,9 +652,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 25.0, 0.0);
validateLoanTransaction(loanDetails, 5, 100.0, 100.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC9: Refund next installment
@@ -699,10 +663,7 @@
// 4. Pay rest on time
@Test
public void uc9() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -759,9 +720,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 4, 125.0, 125.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC10: Refund PD and next installment
@@ -773,10 +732,7 @@
// 4. Pay rest on time
@Test
public void uc10() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -848,9 +804,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 5, 125.0, 125.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC11: Refund Past, pay in advance installments
@@ -861,10 +815,7 @@
// 4. Pay rest on time
@Test
public void uc11() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -913,9 +864,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 25.0, 0.0);
validateLoanTransaction(loanDetails, 3, 100.0, 100.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC12: Refund last installment
@@ -926,10 +875,7 @@
// 4. Pay rest on time
@Test
public void uc12() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -986,9 +932,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 125.0, 0.0);
validateLoanTransaction(loanDetails, 3, 125.0, 125.0, 0.0, 50.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC13: Due apply last installment
@@ -1000,10 +944,7 @@
// 5. Pay rest on time
@Test
public void uc13() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1074,9 +1015,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 75.0, 0.0);
validateLoanTransaction(loanDetails, 5, 50.0, 50.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC14: Refund PD
@@ -1088,10 +1027,7 @@
// 5. Pay rest on time
@Test
public void uc14() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1164,9 +1100,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 5, 175.0, 175.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC15: Goodwill credit PD
@@ -1178,10 +1112,7 @@
// 5. Pay rest on time
@Test
public void uc15() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1251,9 +1182,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 75.0, 0.0);
validateLoanTransaction(loanDetails, 5, 50.0, 50.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC17a: Full refund with CBR
@@ -1265,10 +1194,7 @@
// 5. CBR
@Test
public void uc17a() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1312,9 +1238,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 125.0, 0.0);
validateLoanTransaction(loanDetails, 3, 125.0, 0.0, 125.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC17b: Full refund with CBR
@@ -1326,10 +1250,7 @@
// 5. CBR
@Test
public void uc17b() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1373,9 +1294,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 125.0, 0.0);
validateLoanTransaction(loanDetails, 3, 125.0, 0.0, 125.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC17c: Full refund with CBR
@@ -1387,10 +1306,7 @@
// 5. CBR
@Test
public void uc17c() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.15").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1434,9 +1350,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 125.0, 0.0);
validateLoanTransaction(loanDetails, 3, 125.0, 0.0, 125.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC18: Full refund with CBR (N+1)
@@ -1448,10 +1362,7 @@
// 5. CBR
@Test
public void uc18() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.20").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1530,9 +1441,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 6, 500.0, 0.0, 500.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC24: Merchant issued credit reverse-replay
@@ -1546,10 +1455,7 @@
// 7. Payments
@Test
public void uc24() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.20").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1651,9 +1557,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 125.0, 0.0, 0.0, 0.0);
validateLoanTransaction(loanDetails, 7, 125.0, 125.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC25: Merchant issued credit reverse-replay with uneven balances
@@ -1665,10 +1569,7 @@
// 5. Merchant issued credit
@Test
public void uc25() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.20").dateFormat("yyyy.MM.dd").locale("en"));
+ 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");
@@ -1725,9 +1626,7 @@
validateRepaymentPeriod(loanDetails, 4, 125.0, 115.0, 10.0, 115.0, 0.0);
validateLoanTransaction(loanDetails, 4, 40.0, 40.0, 0.0, 10.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC101: Multiple disbursement test
@@ -1737,11 +1636,7 @@
// 3. Disburse again
@Test
public void uc101() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.20").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("20 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -1808,9 +1703,7 @@
validateRepaymentPeriod(loanDetails, 6, LocalDate.of(2023, 2, 22), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, 0.0, 100.0, 0.0, 0.0,
0.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC102: Multiple disbursement & reschedule test
@@ -1822,11 +1715,7 @@
// 4. Reschedule the 3rd period to be later than the 2nd disbursement
@Test
public void uc102() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.20").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("20 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -1907,9 +1796,7 @@
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 25), 218.75, 100.0, 118.75, 100.0, 0.0);
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2023, 3, 12), 218.75, 0.0, 218.75, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC103: Advanced payment allocation, add charge after maturity -> N+1 installment, repay the loan
@@ -1919,11 +1806,7 @@
// 3. Fully repay the loan
@Test
public void uc103() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -1978,9 +1861,7 @@
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2023, 2, 22), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, 100.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC104: Advanced payment allocation, horizontal repayment processing
@@ -1991,11 +1872,7 @@
// 4. Pay little more than the principal of 1st installment and charge of 2nd installment
@Test
public void uc104() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2063,9 +1940,7 @@
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 125.0, 0.0, 125.0, 0.0, 0.0, 0.0, 25.0, 0.0, 25.0, 0.0, 0.0,
0.0, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC105: Advanced payment allocation, vertical repayment processing
@@ -2076,11 +1951,7 @@
// 4. Pay little more than the charges
@Test
public void uc105() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2148,9 +2019,7 @@
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 125.0, 0.0, 125.0, 0.0, 0.0, 0.0, 25.0, 25.0, 0.0, 0.0, 0.0,
0.0, 0.0, 25.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC106: Advanced payment allocation, horizontal repayment processing, mixed grouping of allocation rules
@@ -2158,11 +2027,7 @@
// 1. Create a Loan product, but the allocation rules are mixed -> expect validation error
@Test
public void uc106() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2176,9 +2041,7 @@
assertNotNull(loanProductErrorData);
assertEquals("mixed.due.type.allocation.rules.are.not.supported.with.horizontal.installment.processing",
loanProductErrorData.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC107: Advanced payment allocation, vertical repayment processing, mixed grouping of allocation rules
@@ -2188,11 +2051,7 @@
// 3. Change to HORIZONTAL loan schedule processing and allocation rules are mixed - > expect validation error
@Test
public void uc107() {
- try {
-
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2220,9 +2079,7 @@
assertEquals(400, exception.getResponse().code());
assertTrue(exception.getMessage()
.contains("mixed.due.type.allocation.rules.are.not.supported.with.horizontal.installment.processing"));
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC108: Advanced payment allocation, progressive loan schedule handling, rounding test
@@ -2231,10 +2088,7 @@
// 2. Submit the loan, and check the generated repayment schedule
@Test
public void uc108() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2313,9 +2167,7 @@
validateRepaymentPeriod(loanDetails, 6, LocalDate.of(2023, 4, 1), 166.65, 0.0, 166.65, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC109: Advanced payment allocation, cumulative loan schedule handling, rounding test
@@ -2324,10 +2176,7 @@
// 2. Submit the loan, and check the generated repayment schedule
@Test
public void uc109() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2406,9 +2255,7 @@
validateRepaymentPeriod(loanDetails, 6, LocalDate.of(2023, 4, 1), 166.65, 0.0, 166.65, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC110: Advanced payment allocation, progressive loan schedule handling, rounding test
@@ -2417,10 +2264,7 @@
// 2. Submit the loan, and check the generated repayment schedule
@Test
public void uc110() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2462,9 +2306,7 @@
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 10.13, 0.0, 10.13, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 10.12, 0.0, 10.12, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC111: Advanced payment allocation, cumulative loan schedule handling, rounding test
@@ -2473,10 +2315,7 @@
// 2. Submit the loan, and check the generated repayment schedule
@Test
public void uc111() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.02.22").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("22 February 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2518,9 +2357,7 @@
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 10.13, 0.0, 10.13, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 10.12, 0.0, 10.12, 0.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC112: Advanced payment allocation, horizontal repayment processing
@@ -2534,10 +2371,7 @@
// 7. Do goodwill credit (in advance payment)
@Test
public void uc112() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.09.01").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("01 September 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2629,9 +2463,7 @@
validateLoanCharge(loanDetails, 1, LocalDate.of(2023, 10, 16), 20.0, 20.0, 0.0);
validateLoanCharge(loanDetails, 2, LocalDate.of(2023, 10, 17), 20.0, 20.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC113: Advanced payment allocation, vertical repayment processing
@@ -2645,10 +2477,7 @@
// 7. Do goodwill credit (in advance payment)
@Test
public void uc113() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.09.01").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("01 September 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2739,9 +2568,7 @@
validateLoanCharge(loanDetails, 1, LocalDate.of(2023, 10, 16), 20.0, 20.0, 0.0);
validateLoanCharge(loanDetails, 2, LocalDate.of(2023, 10, 17), 20.0, 20.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC114: Advanced payment allocation, horizontal repayment processing
@@ -2755,10 +2582,7 @@
// 7. Do merchant issued refund (in advance payment)
@Test
public void uc114() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.09.01").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("01 September 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2849,9 +2673,7 @@
validateLoanCharge(loanDetails, 1, LocalDate.of(2023, 10, 16), 20.0, 10.0, 10.0);
validateLoanCharge(loanDetails, 2, LocalDate.of(2023, 10, 17), 20.0, 10.0, 10.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
}
// UC115: Advanced payment allocation, vertical repayment processing
@@ -2865,10 +2687,7 @@
// 7. Do merchant issued refund (in advance payment)
@Test
public void uc115() {
- try {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
- businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
- .date("2023.09.01").dateFormat("yyyy.MM.dd").locale("en"));
+ runAt("01 September 2023", () -> {
final Account assetAccount = accountHelper.createAssetAccount();
final Account incomeAccount = accountHelper.createIncomeAccount();
@@ -2957,9 +2776,239 @@
validateLoanCharge(loanDetails, 1, LocalDate.of(2023, 10, 16), 20.0, 10.0, 10.0);
validateLoanCharge(loanDetails, 2, LocalDate.of(2023, 10, 17), 20.0, 10.0, 10.0);
assertTrue(loanDetails.getStatus().getActive());
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
- }
+ });
+ }
+
+ // UC116: Advanced payment allocation, horizontal repayment processing, disbursement on due date of an installment
+ // ADVANCED_PAYMENT_ALLOCATION_STRATEGY
+ // 1. Disburse the loan (1000)
+ // 2. Add charge on disbursement date(50)
+ // 3. Repay down payment fully on disbursement date (250)
+ // 4. Disburse on 1st installment due date (400)
+ @Test
+ public void uc116() {
+ runAt("01 January 2023", () -> {
+ final Account assetAccount = accountHelper.createAssetAccount();
+ final Account incomeAccount = accountHelper.createIncomeAccount();
+ final Account expenseAccount = accountHelper.createExpenseAccount();
+ final Account overpaymentAccount = accountHelper.createLiabilityAccount();
+ 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");
+
+ loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
+ .approvedOnDate("01 January 2023").locale("en"));
+
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
+
+ GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1000.0, 0.0, 1000.0, 0.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ // Add Charge Fee
+ Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
+ ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
+ loanTransactionHelper.addChargesForLoan(loanResponse.getLoanId().intValue(),
+ LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "01 January 2023", "50"));
+
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1050.0, 0.0, 1000.0, 0.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 50.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest()
+ .dateFormat(DATETIME_PATTERN).transactionDate("01 January 2023").locale("en").transactionAmount(250.0));
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 800.0, 250.0, 750.0, 250.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 250.0, 0.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 50.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ updateBusinessDate("16 January 2023");
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("16 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(400.0)).locale("en"));
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1200.0, 250.0, 1150.0, 250.0, null);
+ validatePeriod(loanDetails, 0, LocalDate.of(2023, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), LocalDate.of(2023, 1, 1), 750.0, 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), null, 500.0, 250.0, 0.0, 250.0, 50.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 3, LocalDate.of(2023, 1, 16), null, 400.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 4, LocalDate.of(2023, 1, 16), null, 800.0, 100.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 5, LocalDate.of(2023, 1, 31), null, 400.0, 400.0, 0.0, 400.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 6, LocalDate.of(2023, 2, 15), null, 0.0, 400.0, 0.0, 400.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+ });
+ }
+
+ // UC117: Advanced payment allocation, horizontal repayment processing, multi disbursement on due date of an
+ // installment
+ // ADVANCED_PAYMENT_ALLOCATION_STRATEGY
+ // 1. Disburse the loan (1000)
+ // 2. Add charge on disbursement date(50)
+ // 3. Repay down payment fully on disbursement date (250)
+ // 4. Disburse on 1st installment due date (400)
+ // 4. Disburse on 1st installment due date (80)
+ @Test
+ public void uc117() {
+ runAt("01 January 2023", () -> {
+ final Account assetAccount = accountHelper.createAssetAccount();
+ final Account incomeAccount = accountHelper.createIncomeAccount();
+ final Account expenseAccount = accountHelper.createExpenseAccount();
+ final Account overpaymentAccount = accountHelper.createLiabilityAccount();
+ 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");
+
+ loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
+ .approvedOnDate("01 January 2023").locale("en"));
+
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
+
+ GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1000.0, 0.0, 1000.0, 0.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ // Add Charge Fee
+ Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
+ ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "50", false));
+ loanTransactionHelper.addChargesForLoan(loanResponse.getLoanId().intValue(),
+ LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee), "01 January 2023", "50"));
+
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1050.0, 0.0, 1000.0, 0.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 50.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest()
+ .dateFormat(DATETIME_PATTERN).transactionDate("01 January 2023").locale("en").transactionAmount(250.0));
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 800.0, 250.0, 750.0, 250.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 250.0, 0.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 50.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ updateBusinessDate("16 January 2023");
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("16 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(400.0)).locale("en"));
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("16 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(80.0)).locale("en"));
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1280.0, 250.0, 1230.0, 250.0, null);
+ validatePeriod(loanDetails, 0, LocalDate.of(2023, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), LocalDate.of(2023, 1, 1), 750.0, 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), null, 500.0, 250.0, 0.0, 250.0, 50.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 3, LocalDate.of(2023, 1, 16), null, 400.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 4, LocalDate.of(2023, 1, 16), null, 80.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 5, LocalDate.of(2023, 1, 16), null, 880.0, 100.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 6, LocalDate.of(2023, 1, 16), null, 860.0, 20.0, 0.0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 7, LocalDate.of(2023, 1, 31), null, 430.0, 430.0, 0.0, 430.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 8, LocalDate.of(2023, 2, 15), null, 0.0, 430.0, 0.0, 430.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+ });
+ }
+
+ // UC118: Advanced payment allocation, horizontal repayment processing, multi disbursement on disbursement date
+ // ADVANCED_PAYMENT_ALLOCATION_STRATEGY
+ // 1. Disburse the loan (1000)
+ // 2. Disburse on disbursement date (400)
+ @Test
+ public void uc118() {
+ runAt("01 January 2023", () -> {
+ final Account assetAccount = accountHelper.createAssetAccount();
+ final Account incomeAccount = accountHelper.createIncomeAccount();
+ final Account expenseAccount = accountHelper.createExpenseAccount();
+ final Account overpaymentAccount = accountHelper.createLiabilityAccount();
+ 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");
+
+ loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN)
+ .approvedOnDate("01 January 2023").locale("en"));
+
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(1000.0)).locale("en"));
+
+ GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1000.0, 0.0, 1000.0, 0.0, null);
+ validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 250.0, 0.0, 250.0, 0.0, 0.0);
+ validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 250.0, 0.0, 250.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+
+ loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
+ new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
+ .transactionAmount(BigDecimal.valueOf(400.0)).locale("en"));
+ loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
+ validateLoanSummaryBalances(loanDetails, 1400.0, 0.0, 1400.0, 0.0, null);
+ validatePeriod(loanDetails, 0, LocalDate.of(2023, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), null, 400.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
+ null, null, null, null);
+ validatePeriod(loanDetails, 2, LocalDate.of(2023, 1, 1), null, 1150.0, 250.0, 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 3, LocalDate.of(2023, 1, 1), null, 1050.0, 100.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 4, LocalDate.of(2023, 1, 16), null, 700.0, 350.0, 0.0, 350.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 5, LocalDate.of(2023, 1, 31), null, 350.0, 350.0, 0.0, 350.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0, 0.0);
+ validatePeriod(loanDetails, 6, LocalDate.of(2023, 2, 15), null, 0.0, 350.0, 0.0, 350.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0);
+ assertTrue(loanDetails.getStatus().getActive());
+ });
}
private static void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment,
@@ -3085,6 +3134,30 @@
return loanTransactionHelper.getLoanProductId(loanProductJSON);
}
+ private static void validatePeriod(GetLoansLoanIdResponse loanDetails, Integer index, LocalDate dueDate, LocalDate paidDate,
+ Double balanceOfLoan, Double principalDue, Double principalPaid, Double principalOutstanding, Double feeDue, Double feePaid,
+ Double feeOutstanding, Double penaltyDue, Double penaltyPaid, Double penaltyOutstanding, Double interestDue,
+ Double interestPaid, Double interestOutstanding, Double paidInAdvance, Double paidLate) {
+ GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().get(index);
+ assertEquals(dueDate, period.getDueDate());
+ assertEquals(paidDate, period.getObligationsMetOnDate());
+ assertEquals(balanceOfLoan, period.getPrincipalLoanBalanceOutstanding());
+ assertEquals(principalDue, period.getPrincipalDue());
+ assertEquals(principalPaid, period.getPrincipalPaid());
+ assertEquals(principalOutstanding, period.getPrincipalOutstanding());
+ assertEquals(feeDue, period.getFeeChargesDue());
+ assertEquals(feePaid, period.getFeeChargesPaid());
+ assertEquals(feeOutstanding, period.getFeeChargesOutstanding());
+ assertEquals(penaltyDue, period.getPenaltyChargesDue());
+ assertEquals(penaltyPaid, period.getPenaltyChargesPaid());
+ assertEquals(penaltyOutstanding, period.getPenaltyChargesOutstanding());
+ assertEquals(interestDue, period.getInterestDue());
+ assertEquals(interestPaid, period.getInterestPaid());
+ assertEquals(interestOutstanding, period.getInterestOutstanding());
+ assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
+ assertEquals(paidLate, period.getTotalPaidLateForPeriod());
+ }
+
private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, LocalDate dueDate, double principalDue,
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index 6d8ec7b..469f3f3 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -498,4 +498,9 @@
public static final Integer MONTHS = 2;
public static final Integer YEARS = 3;
}
+
+ public void updateBusinessDate(String date) {
+ businessDateHelper.updateBusinessDate(
+ new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
+ }
}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java
index 06275fb..ecbc697 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeOffWithAdvancedPaymentAllocationTest.java
@@ -18,9 +18,6 @@
*/
package org.apache.fineract.integrationtests;
-import static java.lang.Boolean.FALSE;
-import static java.lang.Boolean.TRUE;
-import static org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -42,7 +39,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.fineract.client.models.AdvancedPaymentData;
import org.apache.fineract.client.models.AllowAttributeOverrides;
-import org.apache.fineract.client.models.BusinessDateRequest;
import org.apache.fineract.client.models.ChargeData;
import org.apache.fineract.client.models.ChargeToGLAccountMapper;
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
@@ -61,7 +57,6 @@
import org.apache.fineract.client.models.PostPaymentTypesResponse;
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.accounting.Account;
@@ -83,7 +78,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanAccountChargeOffWithAdvancedPaymentAllocationTest {
+public class LoanAccountChargeOffWithAdvancedPaymentAllocationTest extends BaseLoanIntegrationTest {
private static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
private ResponseSpecification responseSpec;
@@ -777,23 +772,4 @@
return paymentAllocationOrder;
}).toList();
}
-
- private void runAt(String date, Runnable runnable) {
- try {
- GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 42, true);
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, TRUE);
- businessDateHelper.updateBusinessDate(
- new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
- runnable.run();
- } finally {
- GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, FALSE);
- GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 42, false);
- }
- }
-
- private void updateBusinessDate(String date) {
- businessDateHelper.updateBusinessDate(
- new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));
- }
-
}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java
index 9a9ec44..34f17cf 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java
@@ -724,16 +724,16 @@
// down payment period [1]
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3));
- // disbursement period [2]
- verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(2), LocalDate.of(2023, 4, 3), 200.0);
- // down payment period [3]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
- LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
- // regular installment [4]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 3, 150.0, 0.0, 0.0, 150.0, 0.0, 0.0, 0.0, 0.0,
+ // regular installment [2]
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 2, 75.0, 50.0, 50.0, 25.0, 0.0, 0.0, 0.0, 0.0,
false, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3));
+ // disbursement period [3]
+ verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0);
+ // down payment period [4]
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 3, 50.0, 0.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, false,
+ LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
// regular installment [5]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 4, 150.0, 0.0, 0.0, 150.0, 0.0, 0.0, 0.0, 0.0,
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 4, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0,
false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3));
// same day third disbursement with overlapping installment i.e same due date as regular repayment due date
@@ -758,21 +758,21 @@
// down payment period [1]
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3));
- // disbursement period [2]
- verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(2), LocalDate.of(2023, 4, 3), 200.0);
+ // regular installment [2]
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 2, 75.0, 75.0, 75.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
+ LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3));
// disbursement period [3]
verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0);
- // down payment period [4]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
- LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
+ // disbursement period [4]
+ verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(4), LocalDate.of(2023, 4, 3), 200.0);
// down payment period [5]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 25.0, 25.0, 25.0, 0.0, 0.0, 0.0, 0.0,
+ false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
+ // down payment period [6]
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 50.0, 0.0, 0.0, 50.0, 0.0, 0.0, 0.0, 0.0, false,
LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
- // regular installment [6]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0,
- false, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3));
// regular installment [7]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0,
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 375.0, 0.0, 0.0, 375.0, 0.0, 0.0, 0.0, 0.0,
false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3));
// make repayment for fully paying and verify that regular installment gets fully paid on 3rd april
@@ -791,21 +791,21 @@
// down payment period [1]
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3));
- // disbursement period [2]
- verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(2), LocalDate.of(2023, 4, 3), 200.0);
+ // regular installment [2]
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 2, 75.0, 75.0, 75.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
+ LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3));
// disbursement period [3]
verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0);
- // down payment period [4]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
- LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
+ // disbursement period [4]
+ verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(4), LocalDate.of(2023, 4, 3), 200.0);
// down payment period [5]
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
- // regular installment [6]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 225.0, 225.0, 225.0, 0.0, 0.0, 0.0, 0.0, 0.0,
- true, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3));
+ // down payment period [6]
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true,
+ LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3));
// regular installment [7]
- verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0,
+ verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 375.0, 150.0, 150.0, 225.0, 0.0, 0.0, 0.0, 0.0,
false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3));
} finally {
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java
index c13939f..5cbf0e5 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductRepaymentStartDateConfigurationTest.java
@@ -124,7 +124,7 @@
try {
// Set business date
- LocalDate businessDate = LocalDate.of(2023, 03, 3);
+ LocalDate businessDate = LocalDate.of(2023, 3, 3);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
@@ -169,25 +169,25 @@
// first period [2023-03-03 to 2023-04-03]
assertEquals(1, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
- assertEquals(LocalDate.of(2023, 03, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
- assertEquals(LocalDate.of(2023, 04, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+ assertEquals(LocalDate.of(2023, 3, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+ assertEquals(LocalDate.of(2023, 4, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
// second period [2023-04-03 to 2023-05-03]
assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
- assertEquals(LocalDate.of(2023, 04, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
- assertEquals(LocalDate.of(2023, 05, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+ assertEquals(LocalDate.of(2023, 4, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+ assertEquals(LocalDate.of(2023, 5, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
// third period [2023-05-03 to 2023-06-03]
assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
- assertEquals(LocalDate.of(2023, 05, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
- assertEquals(LocalDate.of(2023, 06, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+ assertEquals(LocalDate.of(2023, 5, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+ assertEquals(LocalDate.of(2023, 6, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
assertEquals(333.34, loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
// first disbursement on a future date (7 March 2023)
- LocalDate disbursementDate = LocalDate.of(2023, 03, 7);
+ LocalDate disbursementDate = LocalDate.of(2023, 3, 7);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, disbursementDate);
@@ -208,25 +208,25 @@
// first period [2023-03-03 to 2023-04-03]
assertEquals(1, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
- assertEquals(LocalDate.of(2023, 03, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
- assertEquals(LocalDate.of(2023, 04, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+ assertEquals(LocalDate.of(2023, 3, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+ assertEquals(LocalDate.of(2023, 4, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
assertEquals(166.67, loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
// second period [2023-04-03 to 2023-05-03]
assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
- assertEquals(LocalDate.of(2023, 04, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
- assertEquals(LocalDate.of(2023, 05, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+ assertEquals(LocalDate.of(2023, 4, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+ assertEquals(LocalDate.of(2023, 5, 3), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
assertEquals(166.67, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
// third period [2023-05-03 to 2023-06-03]
assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
- assertEquals(LocalDate.of(2023, 05, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
- assertEquals(LocalDate.of(2023, 06, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+ assertEquals(LocalDate.of(2023, 5, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+ assertEquals(LocalDate.of(2023, 6, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
assertEquals(166.66, loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
// second disbursement next month (7 April 2023)
- disbursementDate = LocalDate.of(2023, 04, 7);
+ disbursementDate = LocalDate.of(2023, 4, 7);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, disbursementDate);
@@ -248,20 +248,20 @@
// first period [2023-03-03 to 2023-04-03]
assertEquals(1, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
- assertEquals(LocalDate.of(2023, 03, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
- assertEquals(LocalDate.of(2023, 04, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+ assertEquals(LocalDate.of(2023, 3, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+ assertEquals(LocalDate.of(2023, 4, 3), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
assertEquals(166.67, loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
// second period [2023-04-03 to 2023-05-03]
assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
- assertEquals(LocalDate.of(2023, 04, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
- assertEquals(LocalDate.of(2023, 05, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+ assertEquals(LocalDate.of(2023, 4, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+ assertEquals(LocalDate.of(2023, 5, 3), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
// third period [2023-05-03 to 2023-06-03]
assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
- assertEquals(LocalDate.of(2023, 05, 3), loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
- assertEquals(LocalDate.of(2023, 06, 3), loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+ assertEquals(LocalDate.of(2023, 5, 3), loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+ assertEquals(LocalDate.of(2023, 6, 3), loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
assertEquals(500.00, loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
} finally {
@@ -278,7 +278,7 @@
try {
// Set business date
- LocalDate businessDate = LocalDate.of(2023, 03, 3);
+ LocalDate businessDate = LocalDate.of(2023, 3, 3);
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
@@ -324,25 +324,25 @@
// first period [2023-03-07 to 2023-04-07]
assertEquals(1, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
- assertEquals(LocalDate.of(2023, 03, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
- assertEquals(LocalDate.of(2023, 04, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+ assertEquals(LocalDate.of(2023, 3, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+ assertEquals(LocalDate.of(2023, 4, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
// second period [2023-04-07 to 2023-05-07]
assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
- assertEquals(LocalDate.of(2023, 04, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
- assertEquals(LocalDate.of(2023, 05, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+ assertEquals(LocalDate.of(2023, 4, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+ assertEquals(LocalDate.of(2023, 5, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
// third period [2023-05-07 to 2023-06-07]
assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
- assertEquals(LocalDate.of(2023, 05, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
- assertEquals(LocalDate.of(2023, 06, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+ assertEquals(LocalDate.of(2023, 5, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+ assertEquals(LocalDate.of(2023, 6, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
assertEquals(333.34, loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
// first disbursement (7 March 2023)
- LocalDate disbursementDate = LocalDate.of(2023, 03, 7);
+ LocalDate disbursementDate = LocalDate.of(2023, 3, 7);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, disbursementDate);
@@ -364,25 +364,25 @@
// first period [2023-03-07 to 2023-04-07]
assertEquals(1, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
- assertEquals(LocalDate.of(2023, 03, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
- assertEquals(LocalDate.of(2023, 04, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+ assertEquals(LocalDate.of(2023, 3, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+ assertEquals(LocalDate.of(2023, 4, 7), loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
assertEquals(166.67, loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
// second period [2023-04-07 to 2023-05-07]
assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
- assertEquals(LocalDate.of(2023, 04, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
- assertEquals(LocalDate.of(2023, 05, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+ assertEquals(LocalDate.of(2023, 4, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+ assertEquals(LocalDate.of(2023, 5, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
assertEquals(166.67, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
// third period [2023-05-07 to 2023-06-07]
assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
- assertEquals(LocalDate.of(2023, 05, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
- assertEquals(LocalDate.of(2023, 06, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+ assertEquals(LocalDate.of(2023, 5, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+ assertEquals(LocalDate.of(2023, 6, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
assertEquals(166.66, loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
// second disbursement next month (7 April 2023)
- disbursementDate = LocalDate.of(2023, 04, 7);
+ disbursementDate = LocalDate.of(2023, 4, 7);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, disbursementDate);
@@ -405,20 +405,20 @@
// first period [2023-03-07 to 2023-04-07]
assertEquals(1, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
- assertEquals(LocalDate.of(2023, 03, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
- assertEquals(LocalDate.of(2023, 04, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+ assertEquals(LocalDate.of(2023, 3, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+ assertEquals(LocalDate.of(2023, 4, 7), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
// second period [2023-04-07 to 2023-05-07]
assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
- assertEquals(LocalDate.of(2023, 04, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
- assertEquals(LocalDate.of(2023, 05, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+ assertEquals(LocalDate.of(2023, 4, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+ assertEquals(LocalDate.of(2023, 5, 7), loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
assertEquals(333.33, loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
// third period [2023-05-07 to 2023-06-07]
assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
- assertEquals(LocalDate.of(2023, 05, 7), loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
- assertEquals(LocalDate.of(2023, 06, 7), loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+ assertEquals(LocalDate.of(2023, 5, 7), loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+ assertEquals(LocalDate.of(2023, 6, 7), loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
assertEquals(333.34, loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
} finally {