FINERACT-2228: Fix incorrect current balance calculation and unintentional transaction reversals
This commit is contained in:
@@ -4449,4 +4449,122 @@ Feature: LoanRepayment
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
| 01 February 2024 | Repayment | 20.0 | 19.42 | 0.58 | 0.0 | 0.0 | 80.58 | false | false |
|
||||
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
|
||||
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
|
||||
|
||||
@TestRailId:C3569
|
||||
Scenario: Verify Loan is fully paid and closed after partial repayments, Merchant issued refund which overpays the loan, partial CBR and reversal of 1st repayment
|
||||
When Admin sets the business date to "28 March 2025"
|
||||
When Admin creates a client with random data
|
||||
When Admin creates a fully customized loan with the following data:
|
||||
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
|
||||
| LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 26 March 2025 | 120 | 35.29 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
|
||||
And Admin successfully approves the loan on "26 March 2025" with "120" amount and expected disbursement date on "26 March 2025"
|
||||
When Admin successfully disburse the loan on "26 March 2025" with "120" EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 |
|
||||
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 26 April 2025 | | 81.15 | 38.85 | 3.53 | 0.0 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 |
|
||||
| 2 | 30 | 26 May 2025 | | 41.16 | 39.99 | 2.39 | 0.0 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 |
|
||||
| 3 | 31 | 26 June 2025 | | 0.0 | 41.16 | 1.21 | 0.0 | 0.0 | 42.37 | 0.0 | 0.0 | 0.0 | 42.37 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 120.0 | 7.13 | 0.0 | 0.0 | 127.13 | 0.0 | 0.0 | 0.0 | 127.13 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
|
||||
And Customer makes "AUTOPAY" repayment on "27 March 2025" with 20 EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 |
|
||||
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 26 April 2025 | | 80.58 | 39.42 | 2.96 | 0.0 | 0.0 | 42.38 | 20.0 | 20.0 | 0.0 | 22.38 |
|
||||
| 2 | 30 | 26 May 2025 | | 40.57 | 40.01 | 2.37 | 0.0 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 |
|
||||
| 3 | 31 | 26 June 2025 | | 0.0 | 40.57 | 1.19 | 0.0 | 0.0 | 41.76 | 0.0 | 0.0 | 0.0 | 41.76 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 120.0 | 6.52 | 0.0 | 0.0 | 126.52 | 20.0 | 20.0 | 0.0 | 106.52 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
|
||||
And Customer makes "AUTOPAY" repayment on "27 March 2025" with 20 EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 |
|
||||
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 26 April 2025 | | 80.01 | 39.99 | 2.39 | 0.0 | 0.0 | 42.38 | 40.0 | 40.0 | 0.0 | 2.38 |
|
||||
| 2 | 30 | 26 May 2025 | | 39.98 | 40.03 | 2.35 | 0.0 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 |
|
||||
| 3 | 31 | 26 June 2025 | | 0.0 | 39.98 | 1.18 | 0.0 | 0.0 | 41.16 | 0.0 | 0.0 | 0.0 | 41.16 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 120.0 | 5.92 | 0.0 | 0.0 | 125.92 | 40.0 | 40.0 | 0.0 | 85.92 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 80.11 | false | false |
|
||||
And Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "27 March 2025" with 120 EUR transaction amount and self-generated Idempotency key
|
||||
Then Loan Repayment schedule has 3 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 |
|
||||
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 26 April 2025 | 27 March 2025 | 80.11 | 39.89 | 0.11 | 0.0 | 0.0 | 40.0 | 40.0 | 40.0 | 0.0 | 0.0 |
|
||||
| 2 | 30 | 26 May 2025 | 27 March 2025 | 42.38 | 37.73 | 0.0 | 0.0 | 0.0 | 37.73 | 37.73 | 37.73 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 26 June 2025 | 27 March 2025 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 | 42.38 | 42.38 | 0.0 | 0.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 120.0 | 0.11 | 0.0 | 0.0 | 120.11 | 120.11 | 120.11 | 0.0 | 0.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 80.11 | false | false |
|
||||
| 27 March 2025 | Merchant Issued Refund | 120.0 | 80.11 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 27 March 2025 | Interest Refund | 0.11 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 27 March 2025 | Accrual Activity | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 28 March 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan status will be "OVERPAID"
|
||||
Then Loan has 0 outstanding amount
|
||||
Then Loan has 40 overpaid amount
|
||||
And Admin makes Credit Balance Refund transaction on "28 March 2025" with 20 EUR transaction amount
|
||||
Then Loan Repayment schedule has 3 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 |
|
||||
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 26 April 2025 | 27 March 2025 | 80.11 | 39.89 | 0.11 | 0.0 | 0.0 | 40.0 | 40.0 | 40.0 | 0.0 | 0.0 |
|
||||
| 2 | 30 | 26 May 2025 | 27 March 2025 | 42.38 | 37.73 | 0.0 | 0.0 | 0.0 | 37.73 | 37.73 | 37.73 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 26 June 2025 | 27 March 2025 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 | 42.38 | 42.38 | 0.0 | 0.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 120.0 | 0.11 | 0.0 | 0.0 | 120.11 | 120.11 | 120.11 | 0.0 | 0.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 80.11 | false | false |
|
||||
| 27 March 2025 | Merchant Issued Refund | 120.0 | 80.11 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 27 March 2025 | Interest Refund | 0.11 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 27 March 2025 | Accrual Activity | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 28 March 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 28 March 2025 | Credit Balance Refund | 20.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan status will be "OVERPAID"
|
||||
Then Loan has 0 outstanding amount
|
||||
Then Loan has 20 overpaid amount
|
||||
When Customer undo "1"th "Repayment" transaction made on "27 March 2025"
|
||||
Then Loan Repayment schedule has 3 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 |
|
||||
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
|
||||
| 1 | 31 | 26 April 2025 | 27 March 2025 | 84.76 | 35.24 | 0.11 | 0.0 | 0.0 | 35.35 | 35.35 | 35.35 | 0.0 | 0.0 |
|
||||
| 2 | 30 | 26 May 2025 | 27 March 2025 | 42.38 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 | 42.38 | 42.38 | 0.0 | 0.0 |
|
||||
| 3 | 31 | 26 June 2025 | 27 March 2025 | 0.0 | 42.38 | 0.0 | 0.0 | 0.0 | 42.38 | 42.38 | 42.38 | 0.0 | 0.0 |
|
||||
Then Loan Repayment schedule has the following data in Total row:
|
||||
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
| 120.0 | 0.11 | 0.0 | 0.0 | 120.11 | 120.11 | 120.11 | 0.0 | 0.0 |
|
||||
Then Loan Transactions tab has the following data:
|
||||
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
|
||||
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | true | false |
|
||||
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | true |
|
||||
| 27 March 2025 | Merchant Issued Refund | 120.0 | 100.11 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||
| 27 March 2025 | Interest Refund | 0.11 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
|
||||
| 27 March 2025 | Accrual Activity | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | true |
|
||||
| 28 March 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 28 March 2025 | Credit Balance Refund | 20.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
|
||||
Then Loan has 0 outstanding amount
|
||||
|
||||
-1
@@ -60,7 +60,6 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa
|
||||
public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) {
|
||||
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan,
|
||||
loan.retrieveListOfTransactionsForReprocessing());
|
||||
loan.doPostLoanTransactionChecks(transactionDate, loan.getLoanLifecycleStateMachine());
|
||||
handleChangedDetail(changedTransactionDetail);
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -275,8 +275,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
continue;
|
||||
}
|
||||
|
||||
Money outstandingBalance = emiCalculator.getOutstandingLoanBalanceOfPeriod(interestScheduleModel, periodDueDate,
|
||||
disbursementDate);
|
||||
Money outstandingBalance = emiCalculator.getOutstandingLoanBalanceOfPeriod(interestScheduleModel, disbursementDate);
|
||||
|
||||
final Money disbursedAmount = Money.of(loanApplicationTerms.getCurrency(), disbursementData.getPrincipal(), mc);
|
||||
final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod
|
||||
|
||||
+1
-2
@@ -119,8 +119,7 @@ public interface EMICalculator {
|
||||
Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
|
||||
@NotNull LocalDate targetDate, boolean includeChargebackInterest);
|
||||
|
||||
Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel interestScheduleModel, LocalDate repaymentPeriodDueDate,
|
||||
LocalDate targetDate);
|
||||
Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel interestScheduleModel, LocalDate targetDate);
|
||||
|
||||
OutstandingDetails getOutstandingAmountsTillDate(ProgressiveLoanInterestScheduleModel model, LocalDate targetDate);
|
||||
|
||||
|
||||
+66
-39
@@ -256,7 +256,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
public PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
|
||||
@NotNull LocalDate targetDate) {
|
||||
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
periodDueDate, targetDate);
|
||||
targetDate);
|
||||
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
|
||||
long notFullyRepaidRepaymentPeriodCount = recalculatedScheduleModelTillDate.repaymentPeriods().stream()
|
||||
.filter(rp -> !rp.isFullyPaid()).count();
|
||||
@@ -265,6 +265,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
if (!targetDate.isAfter(repaymentPeriod.getFromDate())) {
|
||||
if (multiplePeriodIsUnpaid) {
|
||||
repaymentPeriod.setEmi(repaymentPeriod.getOriginalEmi());
|
||||
Money totalOutstandingPrincipal = recalculatedScheduleModelTillDate.getTotalOutstandingPrincipal();
|
||||
Money outstandingPrincipal = repaymentPeriod.getOutstandingPrincipal();
|
||||
// If there are less outstanding principal than anticipated
|
||||
Money emiAdjustment = MathUtil.negativeToZero(outstandingPrincipal.minus(totalOutstandingPrincipal));
|
||||
repaymentPeriod.setEmi(repaymentPeriod.getEmi().minus(emiAdjustment));
|
||||
} else if (repaymentPeriod.isFullyPaid() && onePeriodIsUnpaid) {
|
||||
repaymentPeriod.setEmi(MathUtil.min(repaymentPeriod.getOriginalEmi(), //
|
||||
recalculatedScheduleModelTillDate.getTotalDuePrincipal() //
|
||||
@@ -285,7 +290,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate,
|
||||
@NotNull LocalDate targetDate, boolean includeChargebackInterest) {
|
||||
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
periodDueDate, targetDate);
|
||||
targetDate);
|
||||
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
|
||||
return includeChargebackInterest ? repaymentPeriod.getCalculatedDueInterest()
|
||||
: repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getChargebackInterest(),
|
||||
@@ -293,11 +298,18 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate periodDueDate,
|
||||
LocalDate targetDate) {
|
||||
public Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate targetDate) {
|
||||
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
periodDueDate, targetDate);
|
||||
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
|
||||
targetDate);
|
||||
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate.findRepaymentPeriod(targetDate).orElseGet(() -> {
|
||||
// If target date is after maturity date
|
||||
if (targetDate.isAfter(recalculatedScheduleModelTillDate.getLastRepaymentPeriod().getDueDate())) {
|
||||
return recalculatedScheduleModelTillDate.getLastRepaymentPeriod();
|
||||
} else {
|
||||
// if target date is before 1st disbursement date, we use 1st repayment period
|
||||
return recalculatedScheduleModelTillDate.repaymentPeriods().get(0);
|
||||
}
|
||||
});
|
||||
|
||||
return repaymentPeriod.getOutstandingLoanBalance();
|
||||
}
|
||||
@@ -343,43 +355,58 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
@NotNull
|
||||
private ProgressiveLoanInterestScheduleModel recalculateScheduleModelTillDate(
|
||||
@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate) {
|
||||
@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate targetDate) {
|
||||
MathContext mc = scheduleModel.mc();
|
||||
ProgressiveLoanInterestScheduleModel scheduleModelCopy = scheduleModel.deepCopy(mc);
|
||||
RepaymentPeriod repaymentPeriod = scheduleModelCopy.findRepaymentPeriodByDueDate(periodDueDate).orElseThrow();
|
||||
|
||||
LocalDate adjustedTargetDate = targetDate;
|
||||
InterestPeriod interestPeriod;
|
||||
if (!targetDate.isAfter(repaymentPeriod.getFromDate())) {
|
||||
interestPeriod = repaymentPeriod.getFirstInterestPeriod();
|
||||
adjustedTargetDate = repaymentPeriod.getFromDate();
|
||||
} else if (targetDate.isAfter(repaymentPeriod.getDueDate())) {
|
||||
interestPeriod = repaymentPeriod.getLastInterestPeriod();
|
||||
adjustedTargetDate = repaymentPeriod.getDueDate();
|
||||
boolean isBeforeFirstDisbursement = targetDate.isBefore(scheduleModelCopy.repaymentPeriods().get(0).getFromDate());
|
||||
boolean isAfterMaturityDate = !targetDate.isBefore(scheduleModelCopy.getLastRepaymentPeriod().getDueDate());
|
||||
if (isBeforeFirstDisbursement) {
|
||||
scheduleModelCopy.repaymentPeriods().forEach(rp -> rp.getInterestPeriods().clear());
|
||||
return scheduleModelCopy;
|
||||
} else if (isAfterMaturityDate) {
|
||||
return scheduleModelCopy;
|
||||
} else {
|
||||
interestPeriod = repaymentPeriod.findInterestPeriod(targetDate).orElseThrow();
|
||||
}
|
||||
// TODO use findInterestPeriod
|
||||
scheduleModelCopy.findRepaymentPeriod(targetDate)//
|
||||
.flatMap(rp -> rp.findInterestPeriod(targetDate)).ifPresent(ip -> ip.setDueDate(targetDate)); //
|
||||
interestPeriod.setDueDate(adjustedTargetDate);
|
||||
int index = repaymentPeriod.getInterestPeriods().indexOf(interestPeriod);
|
||||
int nextIdx = index + 1;
|
||||
boolean thereIsInterestPeriodFromDateOnTargetDate = repaymentPeriod.getInterestPeriods().size() > nextIdx
|
||||
&& repaymentPeriod.getInterestPeriods().get(nextIdx).getFromDate().isEqual(targetDate);
|
||||
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.
|
||||
InterestPeriod nextInterestPeriod = repaymentPeriod.getInterestPeriods().get(nextIdx);
|
||||
interestPeriod.addChargebackPrincipalAmount(nextInterestPeriod.getChargebackPrincipal());
|
||||
interestPeriod.addChargebackInterestAmount(nextInterestPeriod.getChargebackInterest());
|
||||
}
|
||||
repaymentPeriod.getInterestPeriods().subList(nextIdx, repaymentPeriod.getInterestPeriods().size()).clear();
|
||||
scheduleModelCopy.repaymentPeriods().forEach(rp -> rp.getInterestPeriods().removeIf(ip -> ip.getDueDate().isAfter(targetDate)));
|
||||
calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), scheduleModelCopy);
|
||||
calculateOutstandingBalance(scheduleModelCopy);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy);
|
||||
RepaymentPeriod repaymentPeriod = scheduleModelCopy.findRepaymentPeriod(targetDate).orElseThrow();
|
||||
|
||||
scheduleModelCopy.repaymentPeriods().forEach(rp -> {
|
||||
if (rp.getDueDate().isAfter(targetDate)) {
|
||||
if (rp.equals(repaymentPeriod)) {
|
||||
rp.findInterestPeriod(targetDate).ifPresent(ip -> {
|
||||
ip.setDueDate(targetDate);
|
||||
int index = rp.getInterestPeriods().indexOf(ip);
|
||||
int nextIdx = index + 1;
|
||||
boolean thereIsInterestPeriodFromDateOnTargetDate = ip.getRepaymentPeriod().getInterestPeriods()
|
||||
.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.
|
||||
InterestPeriod nextInterestPeriod = ip.getRepaymentPeriod().getInterestPeriods().get(nextIdx);
|
||||
ip.addChargebackPrincipalAmount(nextInterestPeriod.getChargebackPrincipal());
|
||||
ip.addChargebackInterestAmount(nextInterestPeriod.getChargebackInterest());
|
||||
}
|
||||
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
|
||||
// period
|
||||
// if so, we need to retain that interest period, but need to update due date to match with from
|
||||
// date -> 0 interest
|
||||
rp.getInterestPeriods().get(0).setDueDate(rp.getInterestPeriods().get(0).getFromDate());
|
||||
if (rp.getInterestPeriods().size() > 1) {
|
||||
rp.getInterestPeriods().subList(1, rp.getInterestPeriods().size()).clear();
|
||||
}
|
||||
} else {
|
||||
rp.getInterestPeriods().clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
calculateRateFactorForPeriods(scheduleModelCopy.repaymentPeriods(), scheduleModelCopy);
|
||||
calculateOutstandingBalance(scheduleModelCopy);
|
||||
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy);
|
||||
}
|
||||
return scheduleModelCopy;
|
||||
}
|
||||
|
||||
|
||||
+9
@@ -42,6 +42,7 @@ import java.util.stream.Collectors;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
@@ -338,6 +339,14 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
return repaymentPeriods().stream().map(RepaymentPeriod::getChargebackPrincipal).reduce(zero, Money::plus);
|
||||
}
|
||||
|
||||
public Money getTotalOutstandingPrincipal() {
|
||||
return MathUtil.negativeToZero(getTotalDuePrincipal().minus(getTotalPaidPrincipal()));
|
||||
}
|
||||
|
||||
public Money getTotalOutstandingInterest() {
|
||||
return MathUtil.negativeToZero(getTotalDueInterest().minus(getTotalPaidInterest()));
|
||||
}
|
||||
|
||||
public Optional<RepaymentPeriod> findRepaymentPeriod(@NotNull LocalDate transactionDate) {
|
||||
return repaymentPeriods.stream() //
|
||||
.filter(period -> isInPeriod(transactionDate, period.getFromDate(), period.getDueDate(), period.isFirstRepaymentPeriod()))//
|
||||
|
||||
+4
@@ -318,4 +318,8 @@ public final class RepaymentPeriod {
|
||||
public boolean isFirstRepaymentPeriod() {
|
||||
return previous == null;
|
||||
}
|
||||
|
||||
public Money getOutstandingPrincipal() {
|
||||
return getDuePrincipal().minus(getPaidPrincipal());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -302,7 +302,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
|
||||
} else {
|
||||
reprocessLoanTransactionsService.reprocessTransactions(loan);
|
||||
}
|
||||
|
||||
loan.doPostLoanTransactionChecks(transactionDate, loan.getLoanLifecycleStateMachine());
|
||||
loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -184,8 +184,8 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
if (!transactionIds.isEmpty()) {
|
||||
this.accountTransfersWritePlatformService.reverseTransfersWithFromAccountTransactions(transactionIds,
|
||||
PortfolioAccountType.LOAN);
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
}
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
|
||||
loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(),
|
||||
false);
|
||||
@@ -267,8 +267,6 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService {
|
||||
writeOffTransaction.reverse();
|
||||
}
|
||||
|
||||
loan.updateLoanSummaryAndStatus();
|
||||
|
||||
if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
|
||||
loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, newTransactionDetail,
|
||||
loanLifecycleStateMachine, transactionForAdjustment, scheduleGeneratorDTO);
|
||||
|
||||
Reference in New Issue
Block a user