Skip to content

Commit 4ae6fd5

Browse files
oleksii-novikov-onixadamsaghy
authored andcommitted
FINERACT-2385: Zero amount reage transaction should not be allowed
1 parent 54e85c7 commit 4ae6fd5

File tree

6 files changed

+177
-14
lines changed

6 files changed

+177
-14
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
*/
1919
package org.apache.fineract.test.stepdef.loan;
2020

21+
import static org.assertj.core.api.Assertions.assertThat;
22+
2123
import io.cucumber.datatable.DataTable;
2224
import io.cucumber.java.en.Then;
2325
import io.cucumber.java.en.When;
2426
import java.io.IOException;
2527
import java.util.List;
2628
import lombok.extern.slf4j.Slf4j;
29+
import okhttp3.ResponseBody;
2730
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
2831
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
2932
import org.apache.fineract.client.models.PostLoansResponse;
@@ -34,6 +37,7 @@
3437
import org.apache.fineract.test.messaging.event.loan.LoanReAgeEvent;
3538
import org.apache.fineract.test.stepdef.AbstractStepDef;
3639
import org.apache.fineract.test.support.TestContextKey;
40+
import org.junit.jupiter.api.Assertions;
3741
import org.springframework.beans.factory.annotation.Autowired;
3842
import retrofit2.Response;
3943

@@ -112,4 +116,34 @@ public void checkLoanReAmortizeBusinessEventCreated() {
112116

113117
eventAssertion.assertEventRaised(LoanReAgeEvent.class, loanId);
114118
}
119+
120+
@When("Admin fails to create a Loan re-aging transaction with error {string} and with the following data:")
121+
public void adminFailsToCreateReAgingTransactionWithError(final String expectedError, final DataTable table) throws IOException {
122+
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
123+
final long loanId = loanResponse.body().getLoanId();
124+
125+
final List<String> data = table.asLists().get(1);
126+
final int frequencyNumber = Integer.parseInt(data.get(0));
127+
final String frequencyType = data.get(1);
128+
final String startDate = data.get(2);
129+
final int numberOfInstallments = Integer.parseInt(data.get(3));
130+
131+
final PostLoansLoanIdTransactionsRequest reAgingRequest = LoanRequestFactory//
132+
.defaultReAgingRequest()//
133+
.frequencyNumber(frequencyNumber)//
134+
.frequencyType(frequencyType)//
135+
.startDate(startDate)//
136+
.numberOfInstallments(numberOfInstallments);//
137+
138+
final Response<PostLoansLoanIdTransactionsResponse> response = loanTransactionsApi
139+
.executeLoanTransaction(loanId, reAgingRequest, "reAge").execute();
140+
141+
try (ResponseBody errorBody = response.errorBody()) {
142+
Assertions.assertNotNull(errorBody);
143+
assertThat(errorBody.string()).contains(expectedError);
144+
}
145+
146+
ErrorHelper.checkFailedApiCall(response, 403);
147+
}
148+
115149
}

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

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3063,3 +3063,106 @@ Feature: LoanReAging
30633063
| NSF fee | true | Specified due date | 01 October 2024 | Flat | 20.0 | 0.0 | 0.0 | 20.0 |
30643064
| NSF fee | true | Specified due date | 01 November 2024 | Flat | 30.0 | 0.0 | 0.0 | 30.0 |
30653065

3066+
Scenario: Verify that Loan re-aging with zero outstanding balance is rejected in real-time
3067+
When Admin sets the business date to "01 January 2024"
3068+
When Admin creates a client with random data
3069+
When Admin set "LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
3070+
When Admin creates a fully customized loan with the following data:
3071+
| 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 |
3072+
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
3073+
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
3074+
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
3075+
Then Loan Repayment schedule has 4 periods, with the following data for periods:
3076+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3077+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3078+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3079+
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3080+
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3081+
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3082+
When Admin sets the business date to "20 February 2024"
3083+
And Customer makes "AUTOPAY" repayment on "20 February 2024" with 750 EUR transaction amount
3084+
Then Loan Repayment schedule has 4 periods, with the following data for periods:
3085+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3086+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3087+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3088+
| 2 | 15 | 16 January 2024 | 20 February 2024 | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3089+
| 3 | 15 | 31 January 2024 | 20 February 2024 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3090+
| 4 | 15 | 15 February 2024 | 20 February 2024 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3091+
And Admin adds "LOAN_NSF_FEE" due date charge with "01 March 2024" due date and 10 EUR transaction amount
3092+
Then Loan Repayment schedule has 5 periods, with the following data for periods:
3093+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3094+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3095+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3096+
| 2 | 15 | 16 January 2024 | 20 February 2024 | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3097+
| 3 | 15 | 31 January 2024 | 20 February 2024 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3098+
| 4 | 15 | 15 February 2024 | 20 February 2024 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3099+
| 5 | 15 | 01 March 2024 | | 0.0 | 0.0 | 0.0 | 0.0 | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
3100+
When Admin fails to create a Loan re-aging transaction with error "error.msg.loan.reage.no.outstanding.balance.to.reage" and with the following data:
3101+
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
3102+
| 1 | MONTHS | 01 March 2024 | 3 |
3103+
Then Loan Repayment schedule has 5 periods, with the following data for periods:
3104+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3105+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3106+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3107+
| 2 | 15 | 16 January 2024 | 20 February 2024 | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3108+
| 3 | 15 | 31 January 2024 | 20 February 2024 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3109+
| 4 | 15 | 15 February 2024 | 20 February 2024 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3110+
| 5 | 15 | 01 March 2024 | | 0.0 | 0.0 | 0.0 | 0.0 | 10.0 | 10.0 | 0.0 | 0.0 | 0.0 | 10.0 |
3111+
Then Loan Transactions tab has the following data:
3112+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
3113+
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
3114+
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
3115+
| 20 February 2024 | Repayment | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
3116+
Then Loan Charges tab has the following data:
3117+
| Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding |
3118+
| NSF fee | true | Specified due date | 01 March 2024 | Flat | 10.0 | 0.0 | 0.0 | 10.0 |
3119+
3120+
Scenario: Verify that zero amount reage transaction is reverted during reverse-replay
3121+
When Admin sets the business date to "01 January 2024"
3122+
When Admin creates a client with random data
3123+
When Admin set "LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
3124+
When Admin creates a fully customized loan with the following data:
3125+
| 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 |
3126+
| LP2_DOWNPAYMENT_AUTO_ADVANCED_PAYMENT_ALLOCATION | 01 January 2024 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
3127+
And Admin successfully approves the loan on "01 January 2024" with "1000" amount and expected disbursement date on "01 January 2024"
3128+
When Admin successfully disburse the loan on "01 January 2024" with "1000" EUR transaction amount
3129+
Then Loan Repayment schedule has 4 periods, with the following data for periods:
3130+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3131+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3132+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3133+
| 2 | 15 | 16 January 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3134+
| 3 | 15 | 31 January 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3135+
| 4 | 15 | 15 February 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3136+
When Admin sets the business date to "20 February 2024"
3137+
When Admin creates a Loan re-aging transaction with the following data:
3138+
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
3139+
| 1 | MONTHS | 01 March 2024 | 3 |
3140+
Then Loan Repayment schedule has 7 periods, with the following data for periods:
3141+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3142+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3143+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3144+
| 2 | 15 | 16 January 2024 | 20 February 2024 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3145+
| 3 | 15 | 31 January 2024 | 20 February 2024 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3146+
| 4 | 15 | 15 February 2024 | 20 February 2024 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
3147+
| 5 | 15 | 01 March 2024 | | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3148+
| 6 | 31 | 01 April 2024 | | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3149+
| 7 | 30 | 01 May 2024 | | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 |
3150+
Then Loan Transactions tab has the following data:
3151+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
3152+
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
3153+
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
3154+
| 20 February 2024 | Re-age | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |
3155+
When Admin sets the business date to "21 February 2024"
3156+
And Customer makes "AUTOPAY" repayment on "19 February 2024" with 750 EUR transaction amount
3157+
Then Loan Repayment schedule has 4 periods, with the following data for periods:
3158+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
3159+
| | | 01 January 2024 | | 1000.0 | | | 0.0 | | 0.0 | 0.0 | | | |
3160+
| 1 | 0 | 01 January 2024 | 01 January 2024 | 750.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 |
3161+
| 2 | 15 | 16 January 2024 | 19 February 2024 | 500.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3162+
| 3 | 15 | 31 January 2024 | 19 February 2024 | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3163+
| 4 | 15 | 15 February 2024 | 19 February 2024 | 0.0 | 250.0 | 0.0 | 0.0 | 0.0 | 250.0 | 250.0 | 0.0 | 250.0 | 0.0 |
3164+
Then Loan Transactions tab has the following data:
3165+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
3166+
| 01 January 2024 | Disbursement | 1000.0 | 0.0 | 0.0 | 0.0 | 0.0 | 1000.0 | false |
3167+
| 01 January 2024 | Down Payment | 250.0 | 250.0 | 0.0 | 0.0 | 0.0 | 750.0 | false |
3168+
| 19 February 2024 | Repayment | 750.0 | 750.0 | 0.0 | 0.0 | 0.0 | 0.0 | false |

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2902,6 +2902,11 @@ private void handleReAge(LoanTransaction loanTransaction, TransactionCtx ctx) {
29022902
loanTransaction.updateComponentsAndTotal(outstandingPrincipalBalance.get(), Money.zero(currency), Money.zero(currency),
29032903
Money.zero(currency));
29042904

2905+
if (outstandingPrincipalBalance.get().isZero()) {
2906+
loanTransaction.reverse();
2907+
return;
2908+
}
2909+
29052910
Money calculatedPrincipal = Money.zero(currency);
29062911
Money adjustCalculatedPrincipal = Money.zero(currency);
29072912
if (outstandingPrincipalBalance.get().isGreaterThanZero()) {

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class LoanReAgingValidator {
5656
public void validateReAge(Loan loan, JsonCommand command) {
5757
validateReAgeRequest(loan, command);
5858
validateReAgeBusinessRules(loan);
59+
validateReAgeOutstandingBalance(loan, command);
5960
}
6061

6162
private void validateReAgeRequest(Loan loan, JsonCommand command) {
@@ -180,4 +181,20 @@ private void throwExceptionIfValidationErrorsExist(List<ApiParameterError> dataV
180181
private boolean transactionHappenedAfterOther(LoanTransaction transaction, LoanTransaction otherTransaction) {
181182
return new ChangeOperation(transaction).compareTo(new ChangeOperation(otherTransaction)) > 0;
182183
}
184+
185+
private void validateReAgeOutstandingBalance(final Loan loan, final JsonCommand command) {
186+
final LocalDate businessDate = getBusinessLocalDate();
187+
final LocalDate startDate = command.dateValueOfParameterNamed(LoanReAgingApiConstants.startDate);
188+
189+
final boolean isBackdated = businessDate.isAfter(startDate);
190+
if (isBackdated) {
191+
return;
192+
}
193+
194+
if (loan.getSummary().getTotalPrincipalOutstanding().compareTo(java.math.BigDecimal.ZERO) == 0) {
195+
throw new GeneralPlatformDomainRuleException("error.msg.loan.reage.no.outstanding.balance.to.reage",
196+
"Loan cannot be re-aged as there are no outstanding balances to be re-aged", loan.getId());
197+
}
198+
}
199+
183200
}

0 commit comments

Comments
 (0)