Skip to content

Commit 726d94d

Browse files
committed
FINERACT-2354: Re-aging: -Interest Handling Option: Equal amortization -- EMICalculator fix & chargeback handling
1 parent ee9da2c commit 726d94d

File tree

11 files changed

+153
-58
lines changed

11 files changed

+153
-58
lines changed

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAgingStepDef.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.time.format.DateTimeFormatter;
3232
import java.util.ArrayList;
3333
import java.util.Arrays;
34-
import java.util.LinkedHashMap;
3534
import java.util.List;
3635
import java.util.Map;
3736
import java.util.Set;
@@ -497,7 +496,7 @@ private List<String> validateRepaymentScheduleTotal(List<String> header, LoanSch
497496
}
498497

499498
PostLoansLoanIdTransactionsRequest setReAgeingRequestProperties(PostLoansLoanIdTransactionsRequest request, List<String> headers,
500-
List<String> values) {
499+
List<String> values) {
501500
for (int i = 0; i < headers.size(); i++) {
502501
String header = headers.get(i).toLowerCase().trim().replaceAll(" ", "");
503502
switch (header) {

fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7743,7 +7743,6 @@ Feature: LoanReAging
77437743
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
77447744
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
77457745
| 01 February 2024 | Accrual Activity | 0.58 | 0.0 | 0.58 | 0.0 | 0.0 | 0.0 | false | false |
7746-
| 01 March 2024 | Accrual Activity | 0.49 | 0.0 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
77477746
| 14 March 2024 | Accrual | 1.27 | 0.0 | 1.27 | 0.0 | 0.0 | 0.0 | false | false |
77487747
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | false | false |
77497748

@@ -7769,7 +7768,6 @@ Feature: LoanReAging
77697768
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
77707769
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
77717770
| 01 February 2024 | Accrual Activity | 0.58 | 0.0 | 0.58 | 0.0 | 0.0 | 0.0 | false | false |
7772-
| 01 March 2024 | Accrual Activity | 0.49 | 0.0 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
77737771
| 14 March 2024 | Accrual | 1.27 | 0.0 | 1.27 | 0.0 | 0.0 | 0.0 | false | false |
77747772
| 15 March 2024 | Re-age | 84.28 | 83.57 | 0.71 | 0.0 | 0.0 | 0.0 | false | false |
77757773
| 01 April 2024 | Payout Refund | 34.02 | 33.78 | 0.24 | 0.0 | 0.0 | 49.79 | false | false |
@@ -7792,28 +7790,6 @@ Feature: LoanReAging
77927790
When Loan Pay-off is made on "02 April 2024"
77937791
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
77947792

7795-
Scenario: XXXVerify Loan re-aging transaction with PR and accrual activity after re-age - interest bearing loan with equal amortization; outstanding payable interest - UC6.4
7796-
When Admin sets the business date to "01 January 2024"
7797-
When Admin creates a client with random data
7798-
When Admin creates a fully customized loan with the following data:
7799-
| 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 |
7800-
| LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_REFUND_INTEREST_RECALC_ACCRUAL_ACTIVITY | 01 January 2024 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS | 1 | MONTHS | 6 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
7801-
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
7802-
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
7803-
7804-
When Admin makes "PAYOUT_REFUND" transaction with "AUTOPAY" payment type on "01 January 2024" with 34.02 EUR transaction amount
7805-
7806-
When Admin sets the business date to "01 February 2024"
7807-
And Customer makes "AUTOPAY" repayment on "01 February 2024" with 17.01 EUR transaction amount
7808-
7809-
When Admin sets the business date to "15 March 2024"
7810-
When Admin creates a Loan re-aging transaction by Loan external ID with the following data:
7811-
| frequencyNumber | frequencyType | startDate | numberOfInstallments | reAgeInterestHandling |
7812-
| 1 | MONTHS | 01 April 2024 | 6 | EQUAL_AMORTIZATION_PAYABLE_INTEREST |
7813-
7814-
When Loan Pay-off is made on "02 April 2024"
7815-
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
7816-
78177793
@TestRailId:C4175 @AdvancedPaymentAllocation
78187794
Scenario: Verify Loan re-aging transaction with BuyDownFee after re-age - interest bearing loan with equal amortization; outstanding payable interest - UC6.5
78197795
When Admin sets the business date to "01 January 2024"
@@ -9716,7 +9692,6 @@ Feature: LoanReAging
97169692
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
97179693
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
97189694
| 01 February 2024 | Accrual Activity | 0.58 | 0.0 | 0.58 | 0.0 | 0.0 | 0.0 | false | false |
9719-
| 01 March 2024 | Accrual Activity | 0.49 | 0.0 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
97209695
| 14 March 2024 | Accrual | 1.27 | 0.0 | 1.27 | 0.0 | 0.0 | 0.0 | false | false |
97219696
| 15 March 2024 | Re-age | 85.08 | 83.57 | 1.51 | 0.0 | 0.0 | 0.0 | false | false |
97229697

@@ -9742,7 +9717,6 @@ Feature: LoanReAging
97429717
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
97439718
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
97449719
| 01 February 2024 | Accrual Activity | 0.58 | 0.0 | 0.58 | 0.0 | 0.0 | 0.0 | false | false |
9745-
| 01 March 2024 | Accrual Activity | 0.49 | 0.0 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
97469720
| 14 March 2024 | Accrual | 1.27 | 0.0 | 1.27 | 0.0 | 0.0 | 0.0 | false | false |
97479721
| 15 March 2024 | Re-age | 85.08 | 83.57 | 1.51 | 0.0 | 0.0 | 0.0 | false | false |
97489722
| 01 April 2024 | Merchant Issued Refund | 34.02 | 33.52 | 0.5 | 0.0 | 0.0 | 50.05 | false | false |

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRepository.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,4 @@ boolean existsNonReversedByLoanAndTypeAndDate(@Param("loan") Loan loan, @Param("
481481
""")
482482
CodeValue fetchClassificationCodeValueByTransactionId(@Param("transactionId") Long transactionId);
483483

484-
@Query("""
485-
SELECT lt FROM LoanTransaction lt
486-
WHERE lt.loan = :loan
487-
AND lt.reversed = false
488-
AND lt.typeOf = :type
489-
AND lt.dateOf <= :onDate
490-
""")
491-
Optional<LoanTransaction> findNonReversedTransactionActiveOnDate(@Param("loan") Loan loan, @Param("type") LoanTransactionType type,
492-
@Param("onDate") LocalDate onDate);
493484
}

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionService.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
3030
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionComparator;
3131
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
32-
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
3332
import org.springframework.stereotype.Service;
3433

3534
@Service
@@ -57,7 +56,4 @@ private Predicate<LoanTransaction> loanTransactionForReprocessingPredicate() {
5756
|| !transaction.isNonMonetaryTransaction() || transaction.isContractTermination());
5857
}
5958

60-
public Optional<LoanTransaction> getActiveReageTransactionOnDate(Loan loan, LocalDate onDate) {
61-
return loanTransactionRepository.findNonReversedTransactionActiveOnDate(loan, LoanTransactionType.REAGE, onDate);
62-
}
6359
}

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,14 +2880,20 @@ private void handleReAge(LoanTransaction loanTransaction, TransactionCtx ctx) {
28802880
} else {
28812881
if (LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_FULL_INTEREST.equals(loanReAgeParameter.getInterestHandlingType())) {
28822882
CommonReAgeSettings settings = new CommonReAgeSettings(false, true, true, true, true);
2883-
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx) {
2883+
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
2884+
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) {
28842885
handleReAgeEqualAmortizationEMICalculator(loanTransaction, settings, progressiveTransactionCtx);
2886+
} else {
2887+
handleReAgeWithCommonStrategy(loanTransaction, settings, ctx);
28852888
}
28862889
} else if (LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_PAYABLE_INTEREST
28872890
.equals(loanReAgeParameter.getInterestHandlingType())) {
28882891
CommonReAgeSettings settings = new CommonReAgeSettings(true, true, true, true, true);
2889-
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx) {
2892+
if (ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
2893+
&& loanTransaction.getLoan().isInterestRecalculationEnabled()) {
28902894
handleReAgeEqualAmortizationEMICalculator(loanTransaction, settings, progressiveTransactionCtx);
2895+
} else {
2896+
handleReAgeWithCommonStrategy(loanTransaction, settings, ctx);
28912897
}
28922898
}
28932899
}
@@ -3204,9 +3210,14 @@ private boolean isInterestRecalculationSupported(TransactionCtx ctx, Loan loan)
32043210
}
32053211

32063212
private BalancesWithPaidInAdvance liftEarlyRepaidBalances(List<LoanRepaymentScheduleInstallment> installments,
3207-
LocalDate transactionDate, MonetaryCurrency currency) {
3213+
LocalDate transactionDate, MonetaryCurrency currency, List<LoanTransaction> alreadyProcessedTransactions) {
32083214
return installments.stream().filter(i -> !i.isDownPayment() && !i.isAdditional() && !i.getDueDate().isBefore(transactionDate))
32093215
.map(installment -> {
3216+
alreadyProcessedTransactions.forEach(tr -> {
3217+
Set<LoanTransactionToRepaymentScheduleMapping> relatedMapping = tr.getLoanTransactionToRepaymentScheduleMappings()
3218+
.stream().filter(m -> m.getInstallment().equals(installment)).collect(Collectors.toSet());
3219+
installment.getLoanTransactionToRepaymentScheduleMappings().addAll(relatedMapping);
3220+
});
32103221
BalancesWithPaidInAdvance res = new BalancesWithPaidInAdvance(installment, currency);
32113222
installment.resetDerivedComponents();
32123223
installment.getLoanTransactionToRepaymentScheduleMappings()
@@ -3257,7 +3268,8 @@ private void handleReAgeEqualAmortizationEMICalculator(LoanTransaction loanTrans
32573268
loanCharge.getAmountOutstanding(currency), numberOfReAgeInstallments, null, currency)))
32583269
.toList();
32593270

3260-
BalancesWithPaidInAdvance paidInAdvanceBalances = liftEarlyRepaidBalances(installments, transactionDate, currency);
3271+
BalancesWithPaidInAdvance paidInAdvanceBalances = liftEarlyRepaidBalances(installments, transactionDate, currency,
3272+
ctx.getAlreadyProcessedTransactions());
32613273

32623274
// TODO add as Parameter here: paidInAdvanceBalances.getAggregatedFeeChargesPortion().isGreaterThanZero() ||
32633275
// paidInAdvanceBalances.getAggregatedPenaltyChargesPortion().isGreaterThanZero()

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.apache.fineract.organisation.monetary.domain.Money;
2929
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
3030
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
31-
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
3231
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
3332
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
3433
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelRepaymentPeriod;

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,13 @@
4141
import org.apache.fineract.organisation.monetary.data.CurrencyData;
4242
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
4343
import org.apache.fineract.organisation.monetary.domain.Money;
44-
import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
4544
import org.apache.fineract.portfolio.common.domain.DaysInMonthType;
4645
import org.apache.fineract.portfolio.common.domain.DaysInYearCustomStrategyType;
4746
import org.apache.fineract.portfolio.common.domain.DaysInYearType;
4847
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
4948
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
5049
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
5150
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
52-
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
5351
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
5452
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
5553
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
@@ -1671,14 +1669,19 @@ public void reAgeEqualAmortization(ProgressiveLoanInterestScheduleModel interest
16711669

16721670
// get creadited principals
16731671

1674-
Money creditedPrincipals = interestSchedule.repaymentPeriods().stream().map(rp->MathUtil.min(rp.getOutstandingPrincipal(),rp.getCreditedPrincipal(), false)).reduce(interestSchedule.zero(), Money::add);
1672+
Money creditedPrincipals = interestSchedule.repaymentPeriods().stream()
1673+
.map(rp -> MathUtil.min(rp.getOutstandingPrincipal(), rp.getCreditedPrincipal(), false))
1674+
.reduce(interestSchedule.zero(), Money::add);
16751675

16761676
// set maturity date to transaction date and remove all repayment periods after it.
16771677
accelerateMaturityDateTo(interestSchedule, transactionDate);
16781678

16791679
// close all open repayment period while keep paid amounts
16801680
interestSchedule.repaymentPeriods().forEach(rp -> {
1681-
rp.getInterestPeriods().getLast().addCreditedPrincipalAmount(MathUtil.min(rp.getOutstandingPrincipal(),rp.getCreditedPrincipal(), false).negated());
1681+
// rp.getInterestPeriods().getLast().addCreditedPrincipalAmount(MathUtil.min(rp.getOutstandingPrincipal(),rp.getCreditedPrincipal(),
1682+
// false).negated());
1683+
rp.getInterestPeriods().getLast()
1684+
.addCreditedInterestAmount(MathUtil.min(rp.getOutstandingInterest(), rp.getCreditedInterest(), false).negated());
16821685
rp.setEmi(rp.getTotalPaidAmount());
16831686
rp.setOutstandingMovedDueToReAging(true);
16841687
});
@@ -1699,12 +1702,13 @@ public void reAgeEqualAmortization(ProgressiveLoanInterestScheduleModel interest
16991702
interestSchedule.zero().getCurrency());
17001703

17011704
// fixCreditedPrincipal
1702-
EqualAmortizationValues creditedPrincipalEAM = calculateEqualAmortizationValues(creditedPrincipals, reageParameter.getNumberOfInstallments(), null, interestSchedule.zero().getCurrency());
1705+
EqualAmortizationValues creditedPrincipalEAM = calculateEqualAmortizationValues(creditedPrincipals,
1706+
reageParameter.getNumberOfInstallments(), null, interestSchedule.zero().getCurrency());
17031707
reAgedRepaymentPeriods.forEach(rp -> {
17041708
Money creditedPrincipalAdjustment = creditedPrincipalEAM.calculateValue(interestSchedule.isLastRepaymentPeriod(rp));
1705-
rp.getInterestPeriods().getLast().addCreditedPrincipalAmount(creditedPrincipalAdjustment);
1706-
//rp.setEmi(rp.getEmi().minus(creditedPrincipalAdjustment));
1707-
//rp.setOriginalEmi(rp.getOriginalEmi().minus(creditedPrincipalAdjustment));
1709+
// rp.getInterestPeriods().getFirst().addCreditedPrincipalAmount(creditedPrincipalAdjustment);
1710+
// rp.setEmi(rp.getEmi().minus(creditedPrincipalAdjustment));
1711+
// rp.setOriginalEmi(rp.getOriginalEmi().minus(creditedPrincipalAdjustment));
17081712
});
17091713

17101714
calculateOutstandingBalance(interestSchedule);

0 commit comments

Comments
 (0)