Skip to content

Commit cc2972f

Browse files
committed
FINERACT-2433: Fix interest and accrual calculation in case of last installment payment
1 parent e17303f commit cc2972f

File tree

9 files changed

+416
-34
lines changed

9 files changed

+416
-34
lines changed

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2600,7 +2600,8 @@ public void initialize() throws Exception {
26002600
.defaultLoanProductsRequestLP2InterestDailyRecalculation()//
26012601
.name(name105)//
26022602
.paymentAllocation(List.of(//
2603-
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT")))
2603+
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"),
2604+
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "LAST_INSTALLMENT")))
26042605
.enableAccrualActivityPosting(true)//
26052606
.chargeOffBehaviour("ZERO_INTEREST");//
26062607
PostLoanProductsResponse responseLoanProductsRequestAdvInterestRecalculationZeroInterestChargeOffBehaviourAccrualActivity = createLoanProductIdempotent(

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

Lines changed: 371 additions & 1 deletion
Large diffs are not rendered by default.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,8 +1496,8 @@ Feature: LoanDelinquency
14961496
Then Loan Transactions tab has the following data:
14971497
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
14981498
| 01 June 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 |
1499-
| 14 July 2024 | Accrual | 10.0 | 0.0 | 10.0 | 0.0 | 0.0 | 0.0 |
1500-
| 15 July 2024 | Repayment | 343.33 | 333.33 | 10.0 | 0.0 | 0.0 | 666.67 |
1499+
| 14 July 2024 | Accrual | 14.19 | 0.0 | 14.19 | 0.0 | 0.0 | 0.0 |
1500+
| 15 July 2024 | Repayment | 343.33 | 333.33 | 10.0 | 0.0 | 0.0 | 666.67 |
15011501
Then Loan has the following LOAN level next payment due data:
15021502
| classification | nextPaymentDueDate | nextPaymentAmount |
15031503
| NO_DELINQUENCY | 01 August 2024 | 343.33 |

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,11 +1017,11 @@ Feature: LoanInterestWaiver
10171017
| 20 | 31 | 18 September 2023 | | 47.25 | 19.23 | 0.7 | 0.0 | 0.0 | 19.93 | 0.0 | 0.0 | 0.0 | 19.93 |
10181018
| 21 | 30 | 18 October 2023 | | 27.99 | 19.26 | 0.67 | 0.0 | 0.0 | 19.93 | 0.0 | 0.0 | 0.0 | 19.93 |
10191019
| 22 | 31 | 18 November 2023 | | 8.76 | 19.23 | 0.7 | 0.0 | 0.0 | 19.93 | 0.0 | 0.0 | 0.0 | 19.93 |
1020-
| 23 | 30 | 18 December 2023 | | 0.0 | 8.76 | 0.67 | 0.0 | 0.0 | 9.43 | 0.0 | 0.0 | 0.0 | 9.43 |
1020+
| 23 | 30 | 18 December 2023 | | 0.0 | 8.76 | 1.37 | 0.0 | 0.0 | 10.13 | 0.0 | 0.0 | 0.0 | 10.13 |
10211021
| 24 | 31 | 18 January 2024 | 20 January 2022 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
10221022
Then Loan Repayment schedule has the following data in Total row:
10231023
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
1024-
| 431.98 | 15.91 | 0.0 | 0.0 | 447.89 | 350.19 | 350.19 | 0.0 | 97.7 |
1024+
| 431.98 | 16.61 | 0.0 | 0.0 | 448.59 | 350.19 | 350.19 | 0.0 | 98.4 |
10251025
Then Loan Transactions tab has the following data:
10261026
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
10271027
| 18 January 2022 | Disbursement | 431.98 | 0.0 | 0.0 | 0.0 | 0.0 | 431.98 | false | false |
@@ -1233,12 +1233,12 @@ Feature: LoanInterestWaiver
12331233
| 7 | 31 | 18 August 2022 | | 171.12 | 37.28 | 0.69 | 0.0 | 0.0 | 37.97 | 29.17 | 29.17 | 0.0 | 8.8 |
12341234
| 8 | 31 | 18 September 2022 | | 133.84 | 37.28 | 0.69 | 0.0 | 0.0 | 37.97 | 29.17 | 29.17 | 0.0 | 8.8 |
12351235
| 9 | 30 | 18 October 2022 | | 96.54 | 37.3 | 0.67 | 0.0 | 0.0 | 37.97 | 29.17 | 29.17 | 0.0 | 8.8 |
1236-
| 10 | 31 | 18 November 2022 | | 58.29 | 38.25 | 0.69 | 0.0 | 0.0 | 38.94 | 29.17 | 29.17 | 0.0 | 9.77 |
1236+
| 10 | 31 | 18 November 2022 | | 58.29 | 38.25 | 2.05 | 0.0 | 0.0 | 40.3 | 29.17 | 29.17 | 0.0 |11.13 |
12371237
| 11 | 30 | 18 December 2022 | 20 January 2022 | 29.12 | 29.17 | 0.0 | 0.0 | 0.0 | 29.17 | 29.17 | 29.17 | 0.0 | 0.0 |
12381238
| 12 | 31 | 18 January 2023 | 20 January 2022 | 0.0 | 29.12 | 0.0 | 0.0 | 0.0 | 29.12 | 29.12 | 29.12 | 0.0 | 0.0 |
12391239
Then Loan Repayment schedule has the following data in Total row:
12401240
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
1241-
| 431.98 | 6.98 | 0.0 | 0.0 | 438.96 | 350.19 | 350.19 | 0.0 | 88.77 |
1241+
| 431.98 | 8.34 | 0.0 | 0.0 | 440.32 | 350.19 | 350.19 | 0.0 | 90.13 |
12421242
Then Loan Transactions tab has the following data:
12431243
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
12441244
| 18 January 2022 | Disbursement | 431.98 | 0.0 | 0.0 | 0.0 | 0.0 | 431.98 |

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8657,25 +8657,26 @@ Then Loan Repayment schedule has 4 periods, with the following data for periods:
86578657
# --- Charge-off ---
86588658
When Admin sets the business date to "15 April 2024"
86598659
And Admin does charge-off the loan on "15 April 2024"
8660-
Then Loan Repayment schedule has 8 periods, with the following data for periods:
8661-
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
8662-
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
8663-
| 1 | 31 | 01 February 2024 | 15 March 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
8664-
| 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
8665-
| 3 | 31 | 01 April 2024 | | 69.84 | 13.73 | 0.48 | 0.0 | 0.0 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 |
8666-
| 4 | 30 | 01 May 2024 | | 55.73 | 14.11 | 0.13 | 0.0 | 0.0 | 14.24 | 0.0 | 0.0 | 0.0 | 14.24 |
8667-
| 5 | 31 | 01 June 2024 | | 41.49 | 14.24 | 0.0 | 0.0 | 0.0 | 14.24 | 0.0 | 0.0 | 0.0 | 14.24 |
8668-
| 6 | 30 | 01 July 2024 | | 27.25 | 14.24 | 0.0 | 0.0 | 0.0 | 14.24 | 0.0 | 0.0 | 0.0 | 14.24 |
8669-
| 7 | 31 | 01 August 2024 | | 13.12 | 14.13 | 0.11 | 0.0 | 0.0 | 14.24 | 0.0 | 0.0 | 0.0 | 14.24 |
8670-
| 8 | 31 | 01 September 2024 | | 0.0 | 13.12 | 0.08 | 0.0 | 0.0 | 13.2 | 0.0 | 0.0 | 0.0 | 13.2 |
8660+
Then Loan Repayment schedule has 9 periods, with the following data for periods:
8661+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
8662+
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
8663+
| 1 | 31 | 01 February 2024 | 01 February 2024| 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
8664+
| 2 | 29 | 01 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
8665+
| 3 | 14 | 15 March 2024 | 15 March 2024 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
8666+
| 4 | 17 | 01 April 2024 | | 69.84 | 13.73 | 0.48 | 0.0 | 0.0 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 |
8667+
| 5 | 30 | 01 May 2024 | | 55.76 | 14.08 | 0.13 | 0.0 | 0.0 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 |
8668+
| 6 | 31 | 01 June 2024 | | 41.55 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 |
8669+
| 7 | 30 | 01 July 2024 | | 27.34 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 |
8670+
| 8 | 31 | 01 August 2024 | | 13.24 | 14.1 | 0.11 | 0.0 | 0.0 | 14.21 | 0.0 | 0.0 | 0.0 | 14.21 |
8671+
| 9 | 31 | 01 September 2024 | | 0.0 | 13.24 | 0.08 | 0.0 | 0.0 | 13.32 | 0.0 | 0.0 | 0.0 | 13.32 |
86718672
And Loan Repayment schedule has the following data in Total row:
86728673
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
86738674
| 100.0 | 1.38 | 0.0 | 0.0 | 101.38 | 17.01 | 0.0 | 0.0 | 84.37 |
86748675
And Loan Transactions tab has the following data:
86758676
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
86768677
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
86778678
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
8678-
| 15 March 2024 | Re-age | 83.7 | 83.57 | 0.13 | 0.0 | 0.0 | 0.0 | false | true |
8679+
| 15 March 2024 | Re-age | 83.78 | 83.57 | 0.21 | 0.0 | 0.0 | 0.0 | false | false |
86798680
| 15 April 2024 | Accrual | 1.19 | 0.0 | 1.19 | 0.0 | 0.0 | 0.0 | false | false |
86808681
| 15 April 2024 | Charge-off | 84.37 | 83.57 | 0.8 | 0.0 | 0.0 | 0.0 | false | false |
86818682
# --- Charge-off undo ---
@@ -8700,7 +8701,7 @@ Then Loan Repayment schedule has 4 periods, with the following data for periods:
87008701
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
87018702
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
87028703
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
8703-
| 15 March 2024 | Re-age | 83.78 | 83.57 | 0.21 | 0.0 | 0.0 | 0.0 | false | true |
8704+
| 15 March 2024 | Re-age | 83.78 | 83.57 | 0.21 | 0.0 | 0.0 | 0.0 | false | false |
87048705
| 15 April 2024 | Accrual | 1.19 | 0.0 | 1.19 | 0.0 | 0.0 | 0.0 | false | false |
87058706
| 15 April 2024 | Charge-off | 84.37 | 83.57 | 0.8 | 0.0 | 0.0 | 0.0 | true | false |
87068707
# --- Close loan ---

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -601,17 +601,20 @@ public void updateModelRepaymentPeriodsDuringReAge(final ProgressiveLoanInterest
601601
public boolean recalculateModelOverdueAmountsTillDate(final ProgressiveLoanInterestScheduleModel scheduleModel,
602602
final LocalDate targetDate, boolean prepayAttempt) {
603603
boolean hasChange = false;
604-
final List<RepaymentPeriod> overdueInstallmentsSortedByInstallmentNumber = findPossiblyOverdueRepaymentPeriods(targetDate,
605-
scheduleModel);
604+
LocalDate recalculatedTargetDate = DateUtils.isAfter(targetDate, scheduleModel.getLastRepaymentPeriod().getDueDate())
605+
? scheduleModel.getLastRepaymentPeriod().getDueDate()
606+
: targetDate;
607+
final List<RepaymentPeriod> overdueInstallmentsSortedByInstallmentNumber = findPossiblyOverdueRepaymentPeriods(
608+
recalculatedTargetDate, scheduleModel);
606609
if (!overdueInstallmentsSortedByInstallmentNumber.isEmpty()) {
607610
final RepaymentPeriod lastPeriod = scheduleModel.getLastRepaymentPeriod();
608-
final RepaymentPeriod currentPeriod = scheduleModel.findRepaymentPeriod(targetDate).orElse(lastPeriod);
611+
final RepaymentPeriod currentPeriod = scheduleModel.findRepaymentPeriod(recalculatedTargetDate).orElse(lastPeriod);
609612
Money overDuePrincipal = scheduleModel.zero();
610613
Money aggregatedOverDuePrincipal = scheduleModel.zero();
611614
for (RepaymentPeriod processingPeriod : overdueInstallmentsSortedByInstallmentNumber) {
612615
// add and subtract outstanding principal
613616
if (!overDuePrincipal.isZero()) {
614-
final boolean currentChanges = adjustOverduePrincipal(targetDate, processingPeriod, overDuePrincipal,
617+
final boolean currentChanges = adjustOverduePrincipal(recalculatedTargetDate, processingPeriod, overDuePrincipal,
615618
aggregatedOverDuePrincipal, scheduleModel, prepayAttempt);
616619

617620
hasChange = hasChange || currentChanges;
@@ -621,15 +624,15 @@ public boolean recalculateModelOverdueAmountsTillDate(final ProgressiveLoanInter
621624
aggregatedOverDuePrincipal = aggregatedOverDuePrincipal.add(overDuePrincipal);
622625
}
623626

624-
if (!currentPeriod.equals(lastPeriod) || !targetDate.isAfter(lastPeriod.getDueDate())) {
625-
final boolean currentChanges = adjustOverduePrincipal(targetDate, currentPeriod, overDuePrincipal,
627+
if (!currentPeriod.equals(lastPeriod) || !recalculatedTargetDate.isAfter(lastPeriod.getDueDate())) {
628+
final boolean currentChanges = adjustOverduePrincipal(recalculatedTargetDate, currentPeriod, overDuePrincipal,
626629
aggregatedOverDuePrincipal, scheduleModel, prepayAttempt);
627630
hasChange = hasChange || currentChanges;
628631

629632
}
630633
if (aggregatedOverDuePrincipal.isGreaterThanZero() && (scheduleModel.lastOverdueBalanceChange() == null
631-
|| scheduleModel.lastOverdueBalanceChange().isBefore(targetDate))) {
632-
scheduleModel.lastOverdueBalanceChange(targetDate);
634+
|| scheduleModel.lastOverdueBalanceChange().isBefore(recalculatedTargetDate))) {
635+
scheduleModel.lastOverdueBalanceChange(recalculatedTargetDate);
633636
}
634637
}
635638

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public class RepaymentPeriod {
8787
@Getter
8888
private final ILoanConfigurationDetails loanProductRelatedDetail;
8989
@JsonExclude
90+
@Setter
9091
private MonetaryCurrency currency;
9192

9293
@Getter
@@ -154,6 +155,10 @@ public static RepaymentPeriod copy(RepaymentPeriod previous, RepaymentPeriod rep
154155
repaymentPeriod.isReAgedEarlyRepaymentHolder(), repaymentPeriod.getReAgedInterest());
155156
newRepaymentPeriod.setCreditedPrincipalMovedDueReAge(repaymentPeriod.getCreditedPrincipalMovedDueReAge());
156157
newRepaymentPeriod.setCreditedInterestMovedDueReAge(repaymentPeriod.getCreditedInterestMovedDueReAge());
158+
newRepaymentPeriod.setTotalDisbursedAmount(repaymentPeriod.getTotalDisbursedAmount());
159+
newRepaymentPeriod.setTotalCapitalizedIncomeAmount(repaymentPeriod.getTotalCapitalizedIncomeAmount());
160+
newRepaymentPeriod.setInterestMoved(repaymentPeriod.isInterestMoved());
161+
newRepaymentPeriod.setCurrency(repaymentPeriod.getCurrency());
157162
// There is always at least 1 interest period, by default with same from-due date as repayment period
158163
for (InterestPeriod interestPeriod : repaymentPeriod.getInterestPeriods()) {
159164
newRepaymentPeriod.getInterestPeriods().add(InterestPeriod.copy(newRepaymentPeriod, interestPeriod, mc));
@@ -169,6 +174,10 @@ public static RepaymentPeriod copyWithoutPaidAmounts(RepaymentPeriod previous, R
169174
repaymentPeriod.isReAged(), repaymentPeriod.isReAgedEarlyRepaymentHolder(), repaymentPeriod.getReAgedInterest());
170175
newRepaymentPeriod.setCreditedPrincipalMovedDueReAge(repaymentPeriod.getCreditedPrincipalMovedDueReAge());
171176
newRepaymentPeriod.setCreditedInterestMovedDueReAge(repaymentPeriod.getCreditedInterestMovedDueReAge());
177+
newRepaymentPeriod.setTotalDisbursedAmount(repaymentPeriod.getTotalDisbursedAmount());
178+
newRepaymentPeriod.setTotalCapitalizedIncomeAmount(repaymentPeriod.getTotalCapitalizedIncomeAmount());
179+
newRepaymentPeriod.setInterestMoved(repaymentPeriod.isInterestMoved());
180+
newRepaymentPeriod.setCurrency(repaymentPeriod.getCurrency());
172181
// There is always at least 1 interest period, by default with same from-due date as repayment period
173182
for (InterestPeriod interestPeriod : repaymentPeriod.getInterestPeriods()) {
174183
var interestPeriodCopy = InterestPeriod.copy(newRepaymentPeriod, interestPeriod);

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionProcessingServiceImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public LoanRepaymentScheduleTransactionProcessor getTransactionProcessor(String
131131

132132
@Override
133133
public LoanScheduleDTO getRecalculatedSchedule(final ScheduleGeneratorDTO generatorDTO, Loan loan) {
134-
if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa() || loan.isChargedOff()) {
134+
if (!loan.isInterestBearingAndInterestRecalculationEnabled() || loan.isNpa()) {
135135
return null;
136136
}
137137
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2745,10 +2745,8 @@ public CommandProcessingResult chargeOff(JsonCommand command) {
27452745
final LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId);
27462746

27472747
if (loan.isInterestBearingAndInterestRecalculationEnabled()) {
2748-
if (loan.isCumulativeSchedule()) {
2749-
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
2750-
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
2751-
}
2748+
final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null);
2749+
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
27522750
reprocessLoanTransactionsService.reprocessTransactions(loan, List.of(chargeOffTransaction));
27532751
loan.addLoanTransaction(chargeOffTransaction);
27542752
} else {

0 commit comments

Comments
 (0)