FINERACT-2281: Interest Activities Not Posting For Loan With Repayment Reversal
This commit is contained in:
@@ -79,6 +79,7 @@ import org.apache.fineract.client.services.GroupsApi;
|
||||
import org.apache.fineract.client.services.HolidaysApi;
|
||||
import org.apache.fineract.client.services.HooksApi;
|
||||
import org.apache.fineract.client.services.ImagesApi;
|
||||
import org.apache.fineract.client.services.InlineJobApi;
|
||||
import org.apache.fineract.client.services.InterestRateChartApi;
|
||||
import org.apache.fineract.client.services.InterestRateSlabAKAInterestBandsApi;
|
||||
import org.apache.fineract.client.services.JournalEntriesApi;
|
||||
@@ -300,6 +301,7 @@ public final class FineractClient {
|
||||
public final ExternalAssetOwnersApi externalAssetOwners;
|
||||
public final ExternalAssetOwnerLoanProductAttributesApi externalAssetOwnerLoanProductAttributes;
|
||||
public final LoanAccountLockApi loanAccountLockApi;
|
||||
public final InlineJobApi inlineJobApi;
|
||||
|
||||
private FineractClient(OkHttpClient okHttpClient, Retrofit retrofit) {
|
||||
this.okHttpClient = okHttpClient;
|
||||
@@ -424,6 +426,7 @@ public final class FineractClient {
|
||||
workingDays = retrofit.create(WorkingDaysApi.class);
|
||||
loanInterestPauseApi = retrofit.create(LoanInterestPauseApi.class);
|
||||
progressiveLoanApi = retrofit.create(ProgressiveLoanApi.class);
|
||||
inlineJobApi = retrofit.create(InlineJobApi.class);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
|
||||
@@ -6037,10 +6037,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
|
||||
Then Loan Repayment schedule has 1 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| | | 01 January 2021 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 01 February 2021 | | 0.0 | 1085.63 | 5.7 | 0.0 | 0.0 | 1091.33 | 1005.7 | 1005.7 | 0.0 | 85.63 |
|
||||
| 1 | 31 | 01 February 2021 | | 0.0 | 1085.63 | 5.9 | 0.0 | 0.0 | 1091.53 | 1005.7 | 1005.7 | 0.0 | 85.83 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 1085.63 | 5.7 | 0.0 | 0.0 | 1091.33 | 1005.7 | 1005.7 | 0.0 | 85.63 |
|
||||
| 1085.63 | 5.9 | 0.0 | 0.0 | 1091.53 | 1005.7 | 1005.7 | 0.0 | 85.83 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2021 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false | false |
|
||||
|
||||
+106
-6
@@ -438,8 +438,108 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
processCreditTransaction(loanTransaction, ctx);
|
||||
}
|
||||
|
||||
protected void handleCreditBalanceRefund(LoanTransaction transaction, TransactionCtx ctx) {
|
||||
super.handleCreditBalanceRefund(transaction, ctx.getCurrency(), ctx.getInstallments(), ctx.getOverpaymentHolder());
|
||||
protected void handleCreditBalanceRefund(LoanTransaction loanTransaction, TransactionCtx ctx) {
|
||||
MonetaryCurrency currency = ctx.getCurrency();
|
||||
List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
|
||||
MoneyHolder overpaymentHolder = ctx.getOverpaymentHolder();
|
||||
|
||||
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
var model = progressiveTransactionCtx.getModel();
|
||||
|
||||
// Copy and paste Logic from super.handleCreditBalanceRefund
|
||||
loanTransaction.resetDerivedComponents();
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = new ArrayList<>();
|
||||
final Comparator<LoanRepaymentScheduleInstallment> byDate = Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate);
|
||||
List<LoanRepaymentScheduleInstallment> installmentToBeProcessed = installments.stream().filter(i -> !i.isDownPayment())
|
||||
.sorted(byDate).toList();
|
||||
final Money zeroMoney = Money.zero(currency);
|
||||
Money transactionAmount = loanTransaction.getAmount(currency);
|
||||
Money principalPortion = MathUtil.negativeToZero(loanTransaction.getAmount(currency).minus(overpaymentHolder.getMoneyObject()));
|
||||
Money repaidAmount = MathUtil.negativeToZero(transactionAmount.minus(principalPortion));
|
||||
loanTransaction.setOverPayments(repaidAmount);
|
||||
overpaymentHolder.setMoneyObject(overpaymentHolder.getMoneyObject().minus(repaidAmount));
|
||||
loanTransaction.updateComponents(principalPortion, zeroMoney, zeroMoney, zeroMoney);
|
||||
|
||||
if (principalPortion.isGreaterThanZero()) {
|
||||
final LocalDate transactionDate = loanTransaction.getTransactionDate();
|
||||
boolean loanTransactionMapped = false;
|
||||
LocalDate pastDueDate = null;
|
||||
for (final LoanRepaymentScheduleInstallment currentInstallment : installmentToBeProcessed) {
|
||||
pastDueDate = currentInstallment.getDueDate();
|
||||
if (!currentInstallment.isAdditional() && DateUtils.isAfter(currentInstallment.getDueDate(), transactionDate)) {
|
||||
|
||||
emiCalculator.creditPrincipal(model, transactionDate, transactionAmount);
|
||||
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
|
||||
|
||||
if (repaidAmount.isGreaterThanZero()) {
|
||||
emiCalculator.payPrincipal(model, currentInstallment.getDueDate(), transactionDate, repaidAmount);
|
||||
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
|
||||
currentInstallment.payPrincipalComponent(transactionDate, repaidAmount);
|
||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
|
||||
currentInstallment, repaidAmount, zeroMoney, zeroMoney, zeroMoney));
|
||||
}
|
||||
loanTransactionMapped = true;
|
||||
break;
|
||||
|
||||
// If already exists an additional installment just update the due date and
|
||||
// principal from the Loan chargeback / CBR transaction
|
||||
} else if (currentInstallment.isAdditional()) {
|
||||
if (DateUtils.isAfter(transactionDate, currentInstallment.getDueDate())) {
|
||||
currentInstallment.updateDueDate(transactionDate);
|
||||
}
|
||||
|
||||
currentInstallment.updateCredits(transactionDate, transactionAmount);
|
||||
if (repaidAmount.isGreaterThanZero()) {
|
||||
currentInstallment.payPrincipalComponent(loanTransaction.getTransactionDate(), repaidAmount);
|
||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
|
||||
currentInstallment, repaidAmount, zeroMoney, zeroMoney, zeroMoney));
|
||||
}
|
||||
loanTransactionMapped = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// New installment will be added (N+1 scenario)
|
||||
if (!loanTransactionMapped) {
|
||||
if (transactionDate.equals(pastDueDate)) {
|
||||
// Transaction is on Maturity date, no additional installment is needed
|
||||
LoanRepaymentScheduleInstallment currentInstallment = installmentToBeProcessed
|
||||
.get(installmentToBeProcessed.size() - 1);
|
||||
|
||||
emiCalculator.creditPrincipal(model, transactionDate, transactionAmount);
|
||||
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
|
||||
|
||||
if (repaidAmount.isGreaterThanZero()) {
|
||||
emiCalculator.payPrincipal(model, currentInstallment.getDueDate(), transactionDate, repaidAmount);
|
||||
updateRepaymentPeriods(loanTransaction, progressiveTransactionCtx);
|
||||
currentInstallment.payPrincipalComponent(transactionDate, repaidAmount);
|
||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction,
|
||||
currentInstallment, repaidAmount, zeroMoney, zeroMoney, zeroMoney));
|
||||
}
|
||||
} else {
|
||||
// transaction is after maturity date, create an additional installment
|
||||
Loan loan = loanTransaction.getLoan();
|
||||
LoanRepaymentScheduleInstallment installment = new LoanRepaymentScheduleInstallment(loan, (installments.size() + 1),
|
||||
pastDueDate, transactionDate, transactionAmount.getAmount(), zeroMoney.getAmount(), zeroMoney.getAmount(),
|
||||
zeroMoney.getAmount(), false, null);
|
||||
installment.markAsAdditional();
|
||||
installment.addToCreditedPrincipal(transactionAmount.getAmount());
|
||||
loan.addLoanRepaymentScheduleInstallment(installment);
|
||||
|
||||
if (repaidAmount.isGreaterThanZero()) {
|
||||
installment.payPrincipalComponent(loanTransaction.getTransactionDate(), repaidAmount);
|
||||
transactionMappings.add(LoanTransactionToRepaymentScheduleMapping.createFrom(loanTransaction, installment,
|
||||
repaidAmount, zeroMoney, zeroMoney, zeroMoney));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
|
||||
}
|
||||
} else {
|
||||
super.handleCreditBalanceRefund(loanTransaction, currency, installments, overpaymentHolder);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasNoCustomCreditAllocationRule(LoanTransaction loanTransaction) {
|
||||
@@ -556,11 +656,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
// handle charge back before or on last installments due date
|
||||
|
||||
if (chargebackAllocation.get(PRINCIPAL).isGreaterThanZero()) {
|
||||
emiCalculator.chargebackPrincipal(model, loanTransaction.getTransactionDate(), chargebackAllocation.get(PRINCIPAL));
|
||||
emiCalculator.creditPrincipal(model, loanTransaction.getTransactionDate(), chargebackAllocation.get(PRINCIPAL));
|
||||
}
|
||||
|
||||
if (chargebackAllocation.get(INTEREST).isGreaterThanZero()) {
|
||||
emiCalculator.chargebackInterest(model, loanTransaction.getTransactionDate(), chargebackAllocation.get(INTEREST));
|
||||
emiCalculator.creditInterest(model, loanTransaction.getTransactionDate(), chargebackAllocation.get(INTEREST));
|
||||
}
|
||||
|
||||
// update repayment periods until maturity date, for principal and interest portions
|
||||
@@ -2171,8 +2271,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (installment != null) {
|
||||
installment.updatePrincipal(rm.getDuePrincipal().getAmount());
|
||||
installment.updateInterestCharged(rm.getDueInterest().getAmount());
|
||||
installment.setCreditedInterest(rm.getChargebackInterest().getAmount());
|
||||
installment.setCreditedPrincipal(rm.getChargebackPrincipal().getAmount());
|
||||
installment.setCreditedInterest(rm.getCreditedInterest().getAmount());
|
||||
installment.setCreditedPrincipal(rm.getCreditedPrincipal().getAmount());
|
||||
installment.updateObligationsMet(ctx.getCurrency(), loanTransaction.getTransactionDate());
|
||||
}
|
||||
});
|
||||
|
||||
+5
-6
@@ -93,17 +93,16 @@ public interface EMICalculator {
|
||||
Money principalAmount);
|
||||
|
||||
/**
|
||||
* This method used for charge back principal portion. This method increases the outstanding balance. This method
|
||||
* creates a calculated "virtual" EMI for the applied period.
|
||||
* This method used for credit principal portion. This method increases the outstanding balance. This method creates
|
||||
* a calculated "virtual" EMI for the applied period.
|
||||
*/
|
||||
void chargebackPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money chargebackPrincipalAmount);
|
||||
void creditPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate, Money creditedPrincipalAmount);
|
||||
|
||||
/**
|
||||
* This method used for charge back interest portion. This method adds extra interest due. This method creates a
|
||||
* This method used for credit interest portion. This method adds extra interest due. This method creates a
|
||||
* calculated "virtual" EMI for the applied period.
|
||||
*/
|
||||
void chargebackInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate, Money chargebackInterestAmount);
|
||||
void creditInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate, Money creditedInterestAmount);
|
||||
|
||||
/**
|
||||
* This method gives back the maximum of the due principal and maximum of the due interest for a requested day.
|
||||
|
||||
+34
-36
@@ -193,31 +193,30 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
repaymentPeriod.ifPresent(rp -> {
|
||||
// If any period total paid > calculated EMI, then set EMI to total paid -> effectively it is marked as
|
||||
// fully paid
|
||||
if (transactionDateIsBefore && rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusChargeback())) {
|
||||
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalChargebackAmount()));
|
||||
if (transactionDateIsBefore && rp.getTotalPaidAmount().isGreaterThan(rp.getEmiPlusCreditedAmounts())) {
|
||||
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
|
||||
} else if (transactionDateIsBefore
|
||||
&& rp.getTotalPaidAmount().isEqualTo(rp.getOriginalEmi().add(rp.getTotalChargebackAmount()))) {
|
||||
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalChargebackAmount()));
|
||||
&& rp.getTotalPaidAmount().isEqualTo(rp.getOriginalEmi().add(rp.getTotalCreditedAmount()))) {
|
||||
rp.setEmi(rp.getTotalPaidAmount().minus(rp.getTotalCreditedAmount()));
|
||||
}
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void addChargebackAmountsToInterestPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money chargebackPrincipalAmount, Money chargeBackInterestAmount) {
|
||||
scheduleModel.repaymentPeriods().stream().filter(checkRepaymentPeriodIsInChargebackRange(scheduleModel, transactionDate))
|
||||
.findFirst()
|
||||
private void addCreditedAmountsToInterestPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money creditedPrincipalAmount, Money creditedInterestAmount) {
|
||||
scheduleModel.repaymentPeriods().stream().filter(checkRepaymentPeriodIsInCreditRange(scheduleModel, transactionDate)).findFirst()
|
||||
.flatMap(repaymentPeriod -> repaymentPeriod.getInterestPeriods().stream()
|
||||
.filter(interestPeriod -> interestPeriod.getFromDate().equals(transactionDate)).reduce((v1, v2) -> v2))
|
||||
.ifPresent(interestPeriod -> {
|
||||
interestPeriod.addChargebackPrincipalAmount(chargebackPrincipalAmount);
|
||||
interestPeriod.addChargebackInterestAmount(chargeBackInterestAmount);
|
||||
interestPeriod.addCreditedPrincipalAmount(creditedPrincipalAmount);
|
||||
interestPeriod.addCreditedInterestAmount(creditedInterestAmount);
|
||||
});
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static Predicate<RepaymentPeriod> checkRepaymentPeriodIsInChargebackRange(ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
private static Predicate<RepaymentPeriod> checkRepaymentPeriodIsInCreditRange(ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
LocalDate transactionDate) {
|
||||
return repaymentPeriod -> scheduleModel.isLastRepaymentPeriod(repaymentPeriod)
|
||||
? !transactionDate.isBefore(repaymentPeriod.getFromDate()) && !transactionDate.isAfter(repaymentPeriod.getDueDate())
|
||||
@@ -225,23 +224,22 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chargebackPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money chargebackPrincipalAmount) {
|
||||
addChargeback(scheduleModel, transactionDate, chargebackPrincipalAmount, scheduleModel.zero());
|
||||
public void creditPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money creditedPrincipalAmount) {
|
||||
addCredit(scheduleModel, transactionDate, creditedPrincipalAmount, scheduleModel.zero());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chargebackInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money chargebackInterestAmount) {
|
||||
addChargeback(scheduleModel, transactionDate, scheduleModel.zero(), chargebackInterestAmount);
|
||||
public void creditInterest(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money creditedInterestAmount) {
|
||||
addCredit(scheduleModel, transactionDate, scheduleModel.zero(), creditedInterestAmount);
|
||||
}
|
||||
|
||||
private void addChargeback(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate,
|
||||
Money chargebackPrincipalAmount, Money chargebackInterestAmount) {
|
||||
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(transactionDate, scheduleModel.zero(), chargebackPrincipalAmount)
|
||||
private void addCredit(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate transactionDate, Money creditedPrincipalAmount,
|
||||
Money creditedInterestAmount) {
|
||||
scheduleModel.changeOutstandingBalanceAndUpdateInterestPeriods(transactionDate, scheduleModel.zero(), creditedPrincipalAmount)
|
||||
.ifPresent(repaymentPeriod -> {
|
||||
addChargebackAmountsToInterestPeriod(scheduleModel, transactionDate, chargebackPrincipalAmount,
|
||||
chargebackInterestAmount);
|
||||
addCreditedAmountsToInterestPeriod(scheduleModel, transactionDate, creditedPrincipalAmount, creditedInterestAmount);
|
||||
calculateRateFactorForRepaymentPeriod(repaymentPeriod, scheduleModel);
|
||||
calculateOutstandingBalance(scheduleModel);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
||||
@@ -288,12 +286,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
@Override
|
||||
@NotNull
|
||||
public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
|
||||
@NotNull LocalDate targetDate, boolean includeChargebackInterest) {
|
||||
@NotNull LocalDate targetDate, boolean includeCreditedInterest) {
|
||||
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
targetDate);
|
||||
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
|
||||
return includeChargebackInterest ? repaymentPeriod.getCalculatedDueInterest()
|
||||
: repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getChargebackInterest(),
|
||||
return includeCreditedInterest ? repaymentPeriod.getCalculatedDueInterest()
|
||||
: repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getCreditedInterest(),
|
||||
recalculatedScheduleModelTillDate.mc());
|
||||
}
|
||||
|
||||
@@ -379,18 +377,18 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
.size() > nextIdx;
|
||||
if (thereIsInterestPeriodFromDateOnTargetDate) {
|
||||
// NOTE: If there is a next interest period with fromDate on the target date
|
||||
// then the related chargeback amounts comes from the next interest period too.
|
||||
// then the related credited amount comes from the next interest period too.
|
||||
InterestPeriod nextInterestPeriod = ip.getRepaymentPeriod().getInterestPeriods().get(nextIdx);
|
||||
ip.addChargebackPrincipalAmount(nextInterestPeriod.getChargebackPrincipal());
|
||||
ip.addChargebackInterestAmount(nextInterestPeriod.getChargebackInterest());
|
||||
ip.addCreditedPrincipalAmount(nextInterestPeriod.getCreditedPrincipal());
|
||||
ip.addCreditedInterestAmount(nextInterestPeriod.getCreditedInterest());
|
||||
}
|
||||
ip.getRepaymentPeriod().getInterestPeriods()
|
||||
.subList(nextIdx, ip.getRepaymentPeriod().getInterestPeriods().size()).clear();
|
||||
});
|
||||
} else if (rp.getPrevious().isPresent() && rp.getPrevious().get().equals(repaymentPeriod)
|
||||
&& (rp.getInterestPeriods().get(0).getChargebackInterest().isGreaterThanZero()
|
||||
|| rp.getInterestPeriods().get(0).getChargebackPrincipal().isGreaterThanZero())) {
|
||||
// NOTE: we need to check whether there is chargeback on the 1st interest period of the next
|
||||
&& (rp.getInterestPeriods().get(0).getCreditedInterest().isGreaterThanZero()
|
||||
|| rp.getInterestPeriods().get(0).getCreditedPrincipal().isGreaterThanZero())) {
|
||||
// NOTE: we need to check whether there is credited on the 1st interest period of the next
|
||||
// period
|
||||
// if so, we need to retain that interest period, but need to update due date to match with from
|
||||
// date -> 0 interest
|
||||
@@ -438,12 +436,12 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
MathContext mc = scheduleModel.mc();
|
||||
Money totalDueInterest = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest).reduce(scheduleModel.zero(),
|
||||
(m1, m2) -> m1.plus(m2, mc)); // 1.46
|
||||
Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmiPlusChargeback).reduce(scheduleModel.zero(),
|
||||
(m1, m2) -> m1.plus(m2, mc)); // 101.48
|
||||
Money totalEMI = scheduleModel.repaymentPeriods().stream().map(RepaymentPeriod::getEmiPlusCreditedAmounts)
|
||||
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 101.48
|
||||
Money totalDisbursedAmount = scheduleModel.repaymentPeriods().stream()
|
||||
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getDisbursementAmount))
|
||||
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)) // 100
|
||||
.plus(scheduleModel.getTotalChargebackPrincipal(), mc); //
|
||||
.plus(scheduleModel.getTotalCreditedPrincipal(), mc); //
|
||||
|
||||
Money diff = totalDisbursedAmount.plus(totalDueInterest, mc).minus(totalEMI, mc);
|
||||
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
|
||||
@@ -451,8 +449,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> {
|
||||
repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc));
|
||||
if (repaymentPeriod.getEmi()
|
||||
.isLessThan(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalChargebackAmount(), mc))) {
|
||||
repaymentPeriod.setEmi(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalChargebackAmount(), mc));
|
||||
.isLessThan(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc))) {
|
||||
repaymentPeriod.setEmi(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc));
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
|
||||
}
|
||||
});
|
||||
|
||||
+16
-14
@@ -53,8 +53,10 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
@Setter
|
||||
private BigDecimal rateFactorTillPeriodDueDate;
|
||||
|
||||
private Money chargebackPrincipal;
|
||||
private Money chargebackInterest;
|
||||
/** Stores credited principals. Related transactions: Chargeback or Credit Balance Refound */
|
||||
private Money creditedPrincipal;
|
||||
/** Stores credited principals. Related transaction: Chargeback */
|
||||
private Money creditedInterest;
|
||||
|
||||
private Money disbursementAmount;
|
||||
private Money balanceCorrectionAmount;
|
||||
@@ -66,8 +68,8 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
|
||||
public static InterestPeriod copy(@NotNull RepaymentPeriod repaymentPeriod, @NotNull InterestPeriod interestPeriod, MathContext mc) {
|
||||
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),
|
||||
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getChargebackPrincipal(),
|
||||
interestPeriod.getChargebackInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getCreditedPrincipal(),
|
||||
interestPeriod.getCreditedInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||
interestPeriod.getOutstandingLoanBalance(), mc, interestPeriod.isPaused());
|
||||
}
|
||||
|
||||
@@ -77,8 +79,8 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
|
||||
public static InterestPeriod copy(@NotNull RepaymentPeriod repaymentPeriod, @NotNull InterestPeriod interestPeriod) {
|
||||
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),
|
||||
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getChargebackPrincipal(),
|
||||
interestPeriod.getChargebackInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||
interestPeriod.getRateFactor(), interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getCreditedPrincipal(),
|
||||
interestPeriod.getCreditedInterest(), interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(),
|
||||
interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc(), interestPeriod.isPaused());
|
||||
}
|
||||
|
||||
@@ -116,17 +118,17 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
this.disbursementAmount = MathUtil.plus(this.disbursementAmount, disbursementAmount, mc);
|
||||
}
|
||||
|
||||
public void addChargebackPrincipalAmount(final Money chargebackPrincipal) {
|
||||
this.chargebackPrincipal = MathUtil.plus(this.chargebackPrincipal, chargebackPrincipal, mc);
|
||||
public void addCreditedPrincipalAmount(final Money creditedPrincipal) {
|
||||
this.creditedPrincipal = MathUtil.plus(this.creditedPrincipal, creditedPrincipal, mc);
|
||||
}
|
||||
|
||||
public void addChargebackInterestAmount(final Money chargebackInterest) {
|
||||
this.chargebackInterest = MathUtil.plus(this.chargebackInterest, chargebackInterest, mc);
|
||||
public void addCreditedInterestAmount(final Money creditedInterest) {
|
||||
this.creditedInterest = MathUtil.plus(this.creditedInterest, creditedInterest, mc);
|
||||
}
|
||||
|
||||
public BigDecimal getCalculatedDueInterest() {
|
||||
if (isPaused) {
|
||||
return chargebackInterest.getAmount();
|
||||
return creditedInterest.getAmount();
|
||||
}
|
||||
|
||||
long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
|
||||
@@ -136,7 +138,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
.multiply(getRateFactorTillPeriodDueDate(), mc) //
|
||||
.divide(BigDecimal.valueOf(lengthTillPeriodDueDate), mc) //
|
||||
.multiply(BigDecimal.valueOf(getLength()), mc); //
|
||||
return MathUtil.negativeToZero(MathUtil.add(chargebackInterest.getAmount(), interestDueTillRepaymentDueDate, mc));
|
||||
return MathUtil.negativeToZero(MathUtil.add(creditedInterest.getAmount(), interestDueTillRepaymentDueDate, mc));
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
@@ -168,10 +170,10 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Include principal like amounts (all disbursement amount + chargeback principal)
|
||||
* Include principal like amounts (all disbursement amount + credited principal)
|
||||
*/
|
||||
public Money getCreditedAmounts() {
|
||||
return getDisbursementAmount().plus(getChargebackPrincipal(), mc);
|
||||
return getDisbursementAmount().plus(getCreditedPrincipal(), mc);
|
||||
}
|
||||
|
||||
public boolean isFirstInterestPeriod() {
|
||||
|
||||
+5
-6
@@ -304,8 +304,7 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the total due interest amount in the whole repayment schedule. Also includes chargeback interest
|
||||
* amount.
|
||||
* Gives back the total due interest amount in the whole repayment schedule. Also includes credited interest amount.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -315,7 +314,7 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
|
||||
/**
|
||||
* Gives back the total due principal amount in the whole repayment schedule based on disbursements. Do not contain
|
||||
* chargeback principal amount.
|
||||
* credited principal amount.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -342,12 +341,12 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the total chargeback principal amount in the whole repayment schedule.
|
||||
* Gives back the total credited principal amount in the whole repayment schedule.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getTotalChargebackPrincipal() {
|
||||
return MathUtil.negativeToZero(repaymentPeriods().stream().map(RepaymentPeriod::getChargebackPrincipal).reduce(zero, Money::plus),
|
||||
public Money getTotalCreditedPrincipal() {
|
||||
return MathUtil.negativeToZero(repaymentPeriods().stream().map(RepaymentPeriod::getCreditedPrincipal).reduce(zero, Money::plus),
|
||||
mc);
|
||||
}
|
||||
|
||||
|
||||
+24
-22
@@ -146,7 +146,7 @@ public final class RepaymentPeriod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back calculated due interest + chargeback interest
|
||||
* Gives back calculated due interest + credited interest
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@@ -169,28 +169,29 @@ public final class RepaymentPeriod {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back due interest + chargeback interest OR paid interest
|
||||
* Gives back due interest + credited interest OR paid interest
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getDueInterest() {
|
||||
if (dueInterestCalculation == null) {
|
||||
// Due interest might be the maximum paid if there is pay-off or early repayment
|
||||
dueInterestCalculation = Memo.of(
|
||||
() -> MathUtil.max(getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? getPaidInterest()
|
||||
: MathUtil.min(getCalculatedDueInterest(), getEmiPlusChargeback(), false), getPaidInterest(), false),
|
||||
() -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
|
||||
dueInterestCalculation = Memo
|
||||
.of(() -> MathUtil.max(
|
||||
getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? getPaidInterest()
|
||||
: MathUtil.min(getCalculatedDueInterest(), getEmiPlusCreditedAmounts(), false),
|
||||
getPaidInterest(), false), () -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
|
||||
}
|
||||
return dueInterestCalculation.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back an EMI amount which includes chargeback amounts as well
|
||||
* Gives back an EMI amount which includes credited amounts as well
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getEmiPlusChargeback() {
|
||||
return getEmi().plus(getTotalChargebackAmount(), mc); //
|
||||
public Money getEmiPlusCreditedAmounts() {
|
||||
return getEmi().plus(getTotalCreditedAmount(), mc); //
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,48 +200,49 @@ public final class RepaymentPeriod {
|
||||
* @return
|
||||
*/
|
||||
public Money getCalculatedDuePrincipal() {
|
||||
return MathUtil.negativeToZero(getEmiPlusChargeback().minus(getCalculatedDueInterest(), mc), mc);
|
||||
return MathUtil.negativeToZero(getEmiPlusCreditedAmounts().minus(getCalculatedDueInterest(), mc), mc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum of chargeback principals
|
||||
* Sum of credited principals
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getChargebackPrincipal() {
|
||||
public Money getCreditedPrincipal() {
|
||||
return MathUtil.negativeToZero(interestPeriods.stream() //
|
||||
.map(InterestPeriod::getChargebackPrincipal) //
|
||||
.map(InterestPeriod::getCreditedPrincipal) //
|
||||
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum of chargeback interests
|
||||
* Sum of credited interests
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getChargebackInterest() {
|
||||
public Money getCreditedInterest() {
|
||||
return MathUtil.negativeToZero(interestPeriods.stream() //
|
||||
.map(InterestPeriod::getChargebackInterest) //
|
||||
.map(InterestPeriod::getCreditedInterest) //
|
||||
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back due principal + chargeback principal or paid principal
|
||||
* Gives back due principal + credited principal or paid principal
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getDuePrincipal() {
|
||||
// Due principal might be the maximum paid if there is pay-off or early repayment
|
||||
return MathUtil.max(MathUtil.negativeToZero(getEmiPlusChargeback().minus(getDueInterest(), mc), mc), getPaidPrincipal(), false);
|
||||
return MathUtil.max(MathUtil.negativeToZero(getEmiPlusCreditedAmounts().minus(getDueInterest(), mc), mc), getPaidPrincipal(),
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back sum of all chargeback principal + chargeback interest
|
||||
* Gives back sum of all credited principal + credited interest
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Money getTotalChargebackAmount() {
|
||||
return getChargebackPrincipal().plus(getChargebackInterest(), mc);
|
||||
public Money getTotalCreditedAmount() {
|
||||
return getCreditedPrincipal().plus(getCreditedInterest(), mc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +255,7 @@ public final class RepaymentPeriod {
|
||||
}
|
||||
|
||||
public boolean isFullyPaid() {
|
||||
return getEmiPlusChargeback().isEqualTo(getTotalPaidAmount());
|
||||
return getEmiPlusCreditedAmounts().isEqualTo(getTotalPaidAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+21
-21
@@ -1305,7 +1305,7 @@ class ProgressiveEMICalculatorTest {
|
||||
final LocalDate startDay = LocalDate.of(2024, 1, 1);
|
||||
|
||||
emiCalculator.payInterest(interestModel, dueDate, startDay.plusDays(3), toMoney(0.56));
|
||||
emiCalculator.chargebackInterest(interestModel, startDay.plusDays(3), toMoney(0.0));
|
||||
emiCalculator.creditInterest(interestModel, startDay.plusDays(3), toMoney(0.0));
|
||||
emiCalculator.addBalanceCorrection(interestModel, startDay.plusDays(3), toMoney(0.0));
|
||||
|
||||
checkDailyInterest(interestModel, dueDate, startDay, 1, 0.19, 0.19);
|
||||
@@ -2403,7 +2403,7 @@ class ProgressiveEMICalculatorTest {
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49));
|
||||
|
||||
// full chargeback on duedate
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(17.01));
|
||||
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(17.01));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2456,7 +2456,7 @@ class ProgressiveEMICalculatorTest {
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49));
|
||||
|
||||
// partial chargeback
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(15.0));
|
||||
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(15.0));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2467,7 +2467,7 @@ class ProgressiveEMICalculatorTest {
|
||||
checkPeriod(interestSchedule, 5, 0, 17.09, 0.005833333333, 0.0991083333276, 0.1, 16.99, 0.0);
|
||||
|
||||
// full chargeback
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(17.01));
|
||||
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), toMoney(17.01));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2520,7 +2520,7 @@ class ProgressiveEMICalculatorTest {
|
||||
emiCalculator.payInterest(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 1), toMoney(0.49));
|
||||
|
||||
// full chargeback on duedate
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, LocalDate.of(2024, 3, 15), toMoney(17.01));
|
||||
emiCalculator.creditPrincipal(interestSchedule, LocalDate.of(2024, 3, 15), toMoney(17.01));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2579,8 +2579,8 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback
|
||||
txnDate = LocalDate.of(2024, 3, 1);
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.chargebackInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2637,8 +2637,8 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback
|
||||
txnDate = LocalDate.of(2024, 7, 1);
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.chargebackInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
|
||||
checkPeriod(interestSchedule, 5, 0, 17.00, 0.005833333333, 0.0985833333276, 0.59, 33.42, 0.0);
|
||||
}
|
||||
@@ -2689,8 +2689,8 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback
|
||||
txnDate = LocalDate.of(2024, 3, 1);
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(14.51));
|
||||
emiCalculator.chargebackInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(14.51));
|
||||
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2747,7 +2747,7 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback
|
||||
txnDate = LocalDate.of(2024, 3, 1);
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(15.0));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(15.0));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -2804,10 +2804,10 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback 1st
|
||||
txnDate = LocalDate.of(2024, 3, 1);
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(15.0));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(15.0));
|
||||
// chargeback 2nd
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.chargebackInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
|
||||
checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.58, 16.43, 83.57);
|
||||
checkPeriod(interestSchedule, 0, 1, 17.01, 0.005833333333, 0.5833333333, 0.58, 16.43, 83.57);
|
||||
@@ -3098,8 +3098,8 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback
|
||||
txnDate = LocalDate.of(2024, 3, 15);
|
||||
emiCalculator.chargebackPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.chargebackInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestSchedule, txnDate, toMoney(16.52));
|
||||
emiCalculator.creditInterest(interestSchedule, txnDate, toMoney(0.49));
|
||||
|
||||
PeriodDueDetails dueAmounts = emiCalculator.getDueAmounts(interestSchedule, LocalDate.of(2024, 4, 1), txnDate);
|
||||
Assertions.assertEquals(33.09, toDouble(interestSchedule.repaymentPeriods().get(2).getDuePrincipal()));
|
||||
@@ -3172,10 +3172,10 @@ class ProgressiveEMICalculatorTest {
|
||||
|
||||
// chargeback
|
||||
txnDate = LocalDate.of(2024, 3, 15);
|
||||
emiCalculator.chargebackPrincipal(interestScheduleExpected, txnDate, toMoney(16.52));
|
||||
emiCalculator.chargebackInterest(interestScheduleExpected, txnDate, toMoney(0.49));
|
||||
emiCalculator.chargebackPrincipal(interestScheduleActual, txnDate, toMoney(16.52));
|
||||
emiCalculator.chargebackInterest(interestScheduleActual, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestScheduleExpected, txnDate, toMoney(16.52));
|
||||
emiCalculator.creditInterest(interestScheduleExpected, txnDate, toMoney(0.49));
|
||||
emiCalculator.creditPrincipal(interestScheduleActual, txnDate, toMoney(16.52));
|
||||
emiCalculator.creditInterest(interestScheduleActual, txnDate, toMoney(0.49));
|
||||
|
||||
verifyAllPeriods(interestScheduleExpected, interestScheduleActual);
|
||||
interestScheduleActual = copyJson(interestScheduleExpected);
|
||||
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ProgressiveLoanCreditBalanceRefundTest extends BaseLoanIntegrationTest {
|
||||
|
||||
Long clientId = ClientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
Long loanProductId = loanProductHelper.createLoanProduct(create4IProgressive()).getResourceId();
|
||||
|
||||
@Test
|
||||
public void testAccrualCreationAfterCBRThenReverseRepayment() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
AtomicReference<Long> reverseRepaymentIdRef = new AtomicReference<>();
|
||||
runAt("13 February 2021", () -> {
|
||||
loanIdRef.set(applyAndApproveProgressiveLoan(clientId, loanProductId, "13 February 2021", 300.0, 37.56, 12, null));
|
||||
|
||||
disburseLoan(loanIdRef.get(), BigDecimal.valueOf(300.0), "13 February 2021");
|
||||
|
||||
verifyRepaymentSchedule(loanIdRef.get(), //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(20.98, 9.39, 30.37, false, "13 March 2021"), //
|
||||
installment(21.64, 8.73, 30.37, false, "13 April 2021"), //
|
||||
installment(22.31, 8.06, 30.37, false, "13 May 2021"), //
|
||||
installment(23.01, 7.36, 30.37, false, "13 June 2021"), //
|
||||
installment(23.73, 6.64, 30.37, false, "13 July 2021"), //
|
||||
installment(24.48, 5.89, 30.37, false, "13 August 2021"), //
|
||||
installment(25.24, 5.13, 30.37, false, "13 September 2021"), //
|
||||
installment(26.03, 4.34, 30.37, false, "13 October 2021"), //
|
||||
installment(26.85, 3.52, 30.37, false, "13 November 2021"), //
|
||||
installment(27.69, 2.68, 30.37, false, "13 December 2021"), //
|
||||
installment(28.55, 1.82, 30.37, false, "13 January 2022"), //
|
||||
installment(29.49, 0.92, 30.41, false, "13 February 2022") //
|
||||
);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment(loanIdRef.get(), "Repayment", "13 February 2021", 60.0);
|
||||
Long repaymentId = loanTransactionHelper.makeLoanRepayment(loanIdRef.get(), "Repayment", "13 February 2021", 40.0)
|
||||
.getResourceId();
|
||||
reverseRepaymentIdRef.set(repaymentId);
|
||||
loanTransactionHelper.makeLoanRepayment(loanIdRef.get(), "MerchantIssuedRefund", "13 February 2021", 300.0);
|
||||
|
||||
verifyRepaymentSchedule(loanIdRef.get(), //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
verifyTransactions(loanIdRef.get(), //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(60.0, "Repayment", "13 February 2021", 240.0, 60.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(40.0, "Repayment", "13 February 2021", 200.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 100.0, false) //
|
||||
);
|
||||
});
|
||||
|
||||
runAt("19 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
loanTransactionHelper.makeLoanRepayment(loanId, "CreditBalanceRefund", "19 February 2021", 100.0);
|
||||
// 0 overpaid
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(60.0, "Repayment", "13 February 2021", 240.0, 60.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(40.0, "Repayment", "13 February 2021", 200.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 100.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, false) //
|
||||
);
|
||||
});
|
||||
runAt("23 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
final Long reverseRepaymentId = reverseRepaymentIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
loanTransactionHelper.reverseLoanTransaction(loanId, reverseRepaymentId, "23 February 2021");
|
||||
// 40 outstanding
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(130.37, 0.98, 40.98, false, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(60.0, "Repayment", "13 February 2021", 240.0, 60.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(40.0, "Repayment", "13 February 2021", 200.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 240.0, 0.0, 0.0, 0.0, 0.0, 60.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 40.0, 40.0, 0.0, 0.0, 0.0, 0.0, 60.0, false) //
|
||||
);
|
||||
});
|
||||
runAt("24 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(60.0, "Repayment", "13 February 2021", 240.0, 60.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(40.0, "Repayment", "13 February 2021", 200.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 240.0, 0.0, 0.0, 0.0, 0.0, 60.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 40.0, 40.0, 0.0, 0.0, 0.0, 0.0, 60.0, false), //
|
||||
transaction(0.18, "Accrual", "23 February 2021", 0.0, 0.0, 0.18, 0.0, 0.0, 0.0, 0.0, false) //
|
||||
);
|
||||
});
|
||||
runAt("28 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(60.0, "Repayment", "13 February 2021", 240.0, 60.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(40.0, "Repayment", "13 February 2021", 200.0, 40.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 240.0, 0.0, 0.0, 0.0, 0.0, 60.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 40.0, 40.0, 0.0, 0.0, 0.0, 0.0, 60.0, false), //
|
||||
transaction(0.18, "Accrual", "23 February 2021", 0.0, 0.0, 0.18, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.04, "Accrual", "24 February 2021", 0.0, 0.0, 0.04, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.05, "Accrual", "25 February 2021", 0.0, 0.0, 0.05, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.04, "Accrual", "26 February 2021", 0.0, 0.0, 0.04, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.05, "Accrual", "27 February 2021", 0.0, 0.0, 0.05, 0.0, 0.0, 0.0, 0.0, false) //
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccrualCreationAfterCBRThenReverseRepaymentThenRepayment() {
|
||||
AtomicReference<Long> loanIdRef = new AtomicReference<>();
|
||||
AtomicReference<Long> reverseRepaymentIdRef = new AtomicReference<>();
|
||||
runAt("13 February 2021", () -> {
|
||||
Long loanId = applyAndApproveProgressiveLoan(clientId, loanProductId, "13 February 2021", 300.0, 37.56, 12, null);
|
||||
loanIdRef.set(loanId);
|
||||
|
||||
disburseLoan(loanId, BigDecimal.valueOf(300.0), "13 February 2021");
|
||||
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(20.98, 9.39, 30.37, false, "13 March 2021"), //
|
||||
installment(21.64, 8.73, 30.37, false, "13 April 2021"), //
|
||||
installment(22.31, 8.06, 30.37, false, "13 May 2021"), //
|
||||
installment(23.01, 7.36, 30.37, false, "13 June 2021"), //
|
||||
installment(23.73, 6.64, 30.37, false, "13 July 2021"), //
|
||||
installment(24.48, 5.89, 30.37, false, "13 August 2021"), //
|
||||
installment(25.24, 5.13, 30.37, false, "13 September 2021"), //
|
||||
installment(26.03, 4.34, 30.37, false, "13 October 2021"), //
|
||||
installment(26.85, 3.52, 30.37, false, "13 November 2021"), //
|
||||
installment(27.69, 2.68, 30.37, false, "13 December 2021"), //
|
||||
installment(28.55, 1.82, 30.37, false, "13 January 2022"), //
|
||||
installment(29.49, 0.92, 30.41, false, "13 February 2022") //
|
||||
);
|
||||
Long resourceId = loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "13 February 2021", 100.0).getResourceId();
|
||||
reverseRepaymentIdRef.set(resourceId);
|
||||
|
||||
loanTransactionHelper.makeLoanRepayment(loanId, "MerchantIssuedRefund", "13 February 2021", 300.0);
|
||||
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Repayment", "13 February 2021", 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 100.0, false) //
|
||||
);
|
||||
|
||||
});
|
||||
runAt("19 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
loanTransactionHelper.makeLoanRepayment(loanId, "CreditBalanceRefund", "19 February 2021", 100.0);
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Repayment", "13 February 2021", 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 200.0, 0.0, 0.0, 0.0, 0.0, 100.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, false) //
|
||||
);
|
||||
});
|
||||
runAt("23 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
|
||||
final Long reverseRepaymentId = reverseRepaymentIdRef.get();
|
||||
loanTransactionHelper.reverseLoanTransaction(loanId, reverseRepaymentId, "23 February 2021");
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(130.37, 2.46, 102.46, false, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Repayment", "13 February 2021", 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 100.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, false) //
|
||||
);
|
||||
});
|
||||
runAt("24 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Repayment", "13 February 2021", 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 100.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.45, "Accrual", "23 February 2021", 0.0, 0.0, 0.45, 0.0, 0.0, 0.0, 0.0, false) //
|
||||
);
|
||||
});
|
||||
runAt("28 February 2021", () -> {
|
||||
final Long loanId = loanIdRef.get();
|
||||
inlineLoanCOBHelper.executeInlineCOB(loanId);
|
||||
loanTransactionHelper.makeLoanRepayment(loanId, "Repayment", "28 February 2021", 101.01);
|
||||
verifyRepaymentSchedule(loanId, //
|
||||
installment(300.0, null, "13 February 2021"), //
|
||||
installment(130.37, 1.01, 0.0, true, "13 March 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 April 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 May 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 June 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 July 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 August 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 September 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 October 2021"), //
|
||||
installment(30.37, 0.0, 0.0, true, "13 November 2021"), //
|
||||
installment(26.67, 0.0, 0.0, true, "13 December 2021"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 January 2022"), //
|
||||
installment(0.0, 0.0, 0.0, true, "13 February 2022") //
|
||||
);
|
||||
verifyTransactions(loanId, //
|
||||
transaction(300.0, "Disbursement", "13 February 2021", 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Repayment", "13 February 2021", 200.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, true), //
|
||||
transaction(300.0, "Merchant Issued Refund", "13 February 2021", 0.0, 300.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(100.0, "Credit Balance Refund", "19 February 2021", 100.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.45, "Accrual", "23 February 2021", 0.0, 0.0, 0.45, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.11, "Accrual", "24 February 2021", 0.0, 0.0, 0.11, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.11, "Accrual", "25 February 2021", 0.0, 0.0, 0.11, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.11, "Accrual", "26 February 2021", 0.0, 0.0, 0.11, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.11, "Accrual", "27 February 2021", 0.0, 0.0, 0.11, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(101.01, "Repayment", "28 February 2021", 0.0, 100.0, 1.01, 0.0, 0.0, 0.0, 0.0, false), //
|
||||
transaction(0.12, "Accrual", "28 February 2021", 0.0, 0.0, 0.12, 0.0, 0.0, 0.0, 0.0, false) //
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
+9
@@ -24,6 +24,10 @@ import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.client.models.InlineJobRequest;
|
||||
import org.apache.fineract.client.models.InlineJobResponse;
|
||||
import org.apache.fineract.client.util.Calls;
|
||||
import org.apache.fineract.integrationtests.common.FineractClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
|
||||
@Slf4j
|
||||
@@ -52,6 +56,11 @@ public class InlineLoanCOBHelper {
|
||||
return Utils.performServerPost(requestSpec, responseSpec, EXECUTE_INLINE_COB_API, buildInlineCOBRequest(loanIds));
|
||||
}
|
||||
|
||||
public InlineJobResponse executeInlineCOB(Long loanId) {
|
||||
return Calls.ok(FineractClientHelper.getFineractClient().inlineJobApi.executeInlineJob("LOAN_COB",
|
||||
new InlineJobRequest().addLoanIdsItem(loanId)));
|
||||
}
|
||||
|
||||
// TODO: Rewrite to use fineract-client instead!
|
||||
// Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
|
||||
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
|
||||
|
||||
Reference in New Issue
Block a user