Skip to content

Commit d018b7c

Browse files
committed
FINERACT-2228: Fix incorrect current balance calculation and unintentional transaction reversals
1 parent 65583a1 commit d018b7c

File tree

7 files changed

+139
-9
lines changed

7 files changed

+139
-9
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,8 @@ public void initialize() throws Exception {
891891
PostLoanProductsRequest loanProductsRequestAdvCustomPaymentAllocationProgressiveLoanSchedule = loanProductsRequestFactory
892892
.defaultLoanProductsRequestLP2InterestDailyRecalculation()//
893893
.name(name44)//
894+
.supportedInterestRefundTypes(Arrays.asList("MERCHANT_ISSUED_REFUND", "PAYOUT_REFUND"))//
895+
.enableAccrualActivityPosting(true) //
894896
.paymentAllocation(List.of(//
895897
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT",
896898
LoanProductPaymentAllocationRule.AllocationTypesEnum.PAST_DUE_INTEREST, //
@@ -905,8 +907,8 @@ public void initialize() throws Exception {
905907
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_PRINCIPAL, //
906908
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_PENALTY, //
907909
LoanProductPaymentAllocationRule.AllocationTypesEnum.IN_ADVANCE_FEE), //
908-
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
909-
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
910+
createPaymentAllocation("GOODWILL_CREDIT", "REAMORTIZATION"), //
911+
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "LAST_INSTALLMENT"), //
910912
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT")));//
911913
Response<PostLoanProductsResponse> responseLoanProductsRequestAdvCustomPaymentAllocationProgressiveLoanSchedule = loanProductsApi
912914
.createLoanProduct(loanProductsRequestAdvCustomPaymentAllocationProgressiveLoanSchedule).execute();

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

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4449,4 +4449,122 @@ Feature: LoanRepayment
44494449
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
44504450
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
44514451
| 01 February 2024 | Repayment | 20.0 | 19.42 | 0.58 | 0.0 | 0.0 | 80.58 | false | false |
4452-
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
4452+
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
4453+
4454+
@TestRailId:C3569
4455+
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
4456+
When Admin sets the business date to "28 March 2025"
4457+
When Admin creates a client with random data
4458+
When Admin creates a fully customized loan with the following data:
4459+
| 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 |
4460+
| 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 |
4461+
And Admin successfully approves the loan on "26 March 2025" with "120" amount and expected disbursement date on "26 March 2025"
4462+
When Admin successfully disburse the loan on "26 March 2025" with "120" EUR transaction amount
4463+
Then Loan Repayment schedule has 3 periods, with the following data for periods:
4464+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4465+
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
4466+
| 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 |
4467+
| 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 |
4468+
| 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 |
4469+
Then Loan Repayment schedule has the following data in Total row:
4470+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4471+
| 120.0 | 7.13 | 0.0 | 0.0 | 127.13 | 0.0 | 0.0 | 0.0 | 127.13 |
4472+
Then Loan Transactions tab has the following data:
4473+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
4474+
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
4475+
And Customer makes "AUTOPAY" repayment on "27 March 2025" with 20 EUR transaction amount
4476+
Then Loan Repayment schedule has 3 periods, with the following data for periods:
4477+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4478+
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
4479+
| 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 |
4480+
| 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 |
4481+
| 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 |
4482+
Then Loan Repayment schedule has the following data in Total row:
4483+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4484+
| 120.0 | 6.52 | 0.0 | 0.0 | 126.52 | 20.0 | 20.0 | 0.0 | 106.52 |
4485+
Then Loan Transactions tab has the following data:
4486+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
4487+
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
4488+
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
4489+
And Customer makes "AUTOPAY" repayment on "27 March 2025" with 20 EUR transaction amount
4490+
Then Loan Repayment schedule has 3 periods, with the following data for periods:
4491+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4492+
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
4493+
| 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 |
4494+
| 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 |
4495+
| 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 |
4496+
Then Loan Repayment schedule has the following data in Total row:
4497+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4498+
| 120.0 | 5.92 | 0.0 | 0.0 | 125.92 | 40.0 | 40.0 | 0.0 | 85.92 |
4499+
Then Loan Transactions tab has the following data:
4500+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
4501+
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
4502+
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
4503+
| 27 March 2025 | Repayment | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 80.11 | false | false |
4504+
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
4505+
Then Loan Repayment schedule has 3 periods, with the following data for periods:
4506+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4507+
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
4508+
| 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 |
4509+
| 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 |
4510+
| 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 |
4511+
Then Loan Repayment schedule has the following data in Total row:
4512+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4513+
| 120.0 | 0.11 | 0.0 | 0.0 | 120.11 | 120.11 | 120.11 | 0.0 | 0.0 |
4514+
Then Loan Transactions tab has the following data:
4515+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
4516+
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
4517+
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
4518+
| 27 March 2025 | Repayment | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 80.11 | false | false |
4519+
| 27 March 2025 | Merchant Issued Refund | 120.0 | 80.11 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
4520+
| 27 March 2025 | Interest Refund | 0.11 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
4521+
| 27 March 2025 | Accrual Activity | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
4522+
| 28 March 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
4523+
Then Loan status will be "OVERPAID"
4524+
Then Loan has 0 outstanding amount
4525+
Then Loan has 40 overpaid amount
4526+
And Admin makes Credit Balance Refund transaction on "28 March 2025" with 20 EUR transaction amount
4527+
Then Loan Repayment schedule has 3 periods, with the following data for periods:
4528+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4529+
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
4530+
| 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 |
4531+
| 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 |
4532+
| 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 |
4533+
Then Loan Repayment schedule has the following data in Total row:
4534+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4535+
| 120.0 | 0.11 | 0.0 | 0.0 | 120.11 | 120.11 | 120.11 | 0.0 | 0.0 |
4536+
Then Loan Transactions tab has the following data:
4537+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
4538+
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
4539+
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | false |
4540+
| 27 March 2025 | Repayment | 20.0 | 20.0 | 0.0 | 0.0 | 0.0 | 80.11 | false | false |
4541+
| 27 March 2025 | Merchant Issued Refund | 120.0 | 80.11 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
4542+
| 27 March 2025 | Interest Refund | 0.11 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
4543+
| 27 March 2025 | Accrual Activity | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
4544+
| 28 March 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
4545+
| 28 March 2025 | Credit Balance Refund | 20.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
4546+
Then Loan status will be "OVERPAID"
4547+
Then Loan has 0 outstanding amount
4548+
Then Loan has 20 overpaid amount
4549+
When Customer undo "1"th "Repayment" transaction made on "27 March 2025"
4550+
Then Loan Repayment schedule has 3 periods, with the following data for periods:
4551+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4552+
| | | 26 March 2025 | | 120.0 | | | 0.0 | | 0.0 | 0.0 | | | |
4553+
| 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 |
4554+
| 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 |
4555+
| 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 |
4556+
Then Loan Repayment schedule has the following data in Total row:
4557+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
4558+
| 120.0 | 0.11 | 0.0 | 0.0 | 120.11 | 120.11 | 120.11 | 0.0 | 0.0 |
4559+
Then Loan Transactions tab has the following data:
4560+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
4561+
| 26 March 2025 | Disbursement | 120.0 | 0.0 | 0.0 | 0.0 | 0.0 | 120.0 | false | false |
4562+
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | true | false |
4563+
| 27 March 2025 | Repayment | 20.0 | 19.89 | 0.11 | 0.0 | 0.0 | 100.11 | false | true |
4564+
| 27 March 2025 | Merchant Issued Refund | 120.0 | 100.11 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
4565+
| 27 March 2025 | Interest Refund | 0.11 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | true |
4566+
| 27 March 2025 | Accrual Activity | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | true |
4567+
| 28 March 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 | false | false |
4568+
| 28 March 2025 | Credit Balance Refund | 20.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
4569+
Then Loan status will be "CLOSED_OBLIGATIONS_MET"
4570+
Then Loan has 0 outstanding amount

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ public void reprocessParticularTransactions(final Loan loan, final List<LoanTran
6060
public void reprocessTransactionsWithPostTransactionChecks(final Loan loan, final LocalDate transactionDate) {
6161
final ChangedTransactionDetail changedTransactionDetail = reprocessTransactionsAndFetchChangedTransactions(loan,
6262
loan.retrieveListOfTransactionsForReprocessing());
63-
loan.doPostLoanTransactionChecks(transactionDate, loan.getLoanLifecycleStateMachine());
6463
handleChangedDetail(changedTransactionDetail);
6564
}
6665

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,11 @@ public PeriodDueDetails getDueAmounts(@NotNull ProgressiveLoanInterestScheduleMo
264264
boolean onePeriodIsUnpaid = notFullyRepaidRepaymentPeriodCount == 1L;
265265
if (!targetDate.isAfter(repaymentPeriod.getFromDate())) {
266266
if (multiplePeriodIsUnpaid) {
267-
repaymentPeriod.setEmi(repaymentPeriod.getOriginalEmi());
267+
Money totalOutstanding = recalculatedScheduleModelTillDate.getTotalOutstandingPrincipal()
268+
.plus(recalculatedScheduleModelTillDate.getTotalOutstandingInterest());
269+
Money newEmi = totalOutstanding.isLessThan(repaymentPeriod.getOriginalEmi()) ? totalOutstanding
270+
: repaymentPeriod.getOriginalEmi();
271+
repaymentPeriod.setEmi(newEmi);
268272
} else if (repaymentPeriod.isFullyPaid() && onePeriodIsUnpaid) {
269273
repaymentPeriod.setEmi(MathUtil.min(repaymentPeriod.getOriginalEmi(), //
270274
recalculatedScheduleModelTillDate.getTotalDuePrincipal() //

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import lombok.Data;
4343
import lombok.experimental.Accessors;
4444
import org.apache.fineract.infrastructure.core.service.DateUtils;
45+
import org.apache.fineract.infrastructure.core.service.MathUtil;
4546
import org.apache.fineract.organisation.monetary.domain.Money;
4647
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
4748
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
@@ -338,6 +339,14 @@ public Money getTotalChargebackPrincipal() {
338339
return repaymentPeriods().stream().map(RepaymentPeriod::getChargebackPrincipal).reduce(zero, Money::plus);
339340
}
340341

342+
public Money getTotalOutstandingPrincipal() {
343+
return MathUtil.negativeToZero(getTotalDuePrincipal().minus(getTotalPaidPrincipal()));
344+
}
345+
346+
public Money getTotalOutstandingInterest() {
347+
return MathUtil.negativeToZero(getTotalDueInterest().minus(getTotalPaidInterest()));
348+
}
349+
341350
public Optional<RepaymentPeriod> findRepaymentPeriod(@NotNull LocalDate transactionDate) {
342351
return repaymentPeriods.stream() //
343352
.filter(period -> isInPeriod(transactionDate, period.getFromDate(), period.getDueDate(), period.isFirstRepaymentPeriod()))//

0 commit comments

Comments
 (0)