Skip to content

Commit fb90c01

Browse files
peter-kovacs-dpcadamsaghy
authored andcommitted
FINERACT-2326: Loan contract termination same disbursement date - E2E tests
1 parent 7a647e6 commit fb90c01

File tree

8 files changed

+92
-36
lines changed

8 files changed

+92
-36
lines changed

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ public enum DefaultLoanProduct implements LoanProduct {
146146
LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_DELINQUENT_REASON_INTEREST_RECALC_CAPITALIZED_INCOME, //
147147
LP2_ADV_PYMNT_360_30_INTEREST_RECALCULATION_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY, //
148148
LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION, //
149+
LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION_INT_RECOGNITION, //
149150
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_OVER_APPLIED_PERCENTAGE_CAPITALIZED_INCOME, //
150151
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_OVER_APPLIED_FLAT_CAPITALIZED_INCOME, //
151152
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_APPROVED_OVER_APPLIED_PERCENTAGE_CAPITALIZED_INCOME, //

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ public abstract class TestContextKey {
257257
public static final String LOAN_CAPITALIZED_INCOME_ADJUSTMENT_RESPONSE = "loanCapitalizedIncomeAdjustmentResponse";
258258
public static final String LOAN_INTEREST_REFUND_RESPONSE = "loanInterestRefundResponse";
259259
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationContractTermination";
260+
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION_INT_RECOGNITION = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyInterestRecalculationContractTerminationIntRecognition";
260261
public static final String LOAN_CONTRACT_TERMINATION_RESPONSE = "loanContractTerminationResponse";
261262
public static final String LOAN_UNDO_CONTRACT_TERMINATION_RESPONSE = "loanUndoContractTerminationResponse";
262263
public static final String LOAN_BUY_DOWN_FEE_RESPONSE = "loanBuyDownFeeResponse";

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,3 +1317,53 @@ Feature: Contract Termination
13171317
| 31 March 2024 | Accrual Adjustment | 0.15 | 0.0 | 0.15 | 0.0 | 0.0 | 0.0 | false | false |
13181318
| 31 March 2024 | Contract Termination | 57.37 | 57.05 | 0.32 | 0.0 | 0.0 | 0.0 | true | true |
13191319
And Global configuration "is-principal-compounding-disabled-for-overdue-loans" is disabled
1320+
1321+
@TestRailId:C4133
1322+
Scenario: Contract termination on disbursement date
1323+
When Admin sets the business date to "01 January 2025"
1324+
And Admin creates a client with random data
1325+
And Admin creates a fully customized loan with the following data:
1326+
| 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 |
1327+
| LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION | 01 January 2025 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 4 | MONTHS | 1 | MONTHS | 4 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
1328+
And Admin successfully approves the loan on "01 January 2025" with "100" amount and expected disbursement date on "01 January 2025"
1329+
And Admin successfully disburse the loan on "01 January 2025" with "100" EUR transaction amount
1330+
And Admin successfully terminates loan contract
1331+
Then Loan Repayment schedule has 4 periods, with the following data for periods:
1332+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
1333+
| | | 01 January 2025 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
1334+
| 1 | 31 | 01 February 2025 | | 75.21 | 24.79 | 0.58 | 0.0 | 0.0 | 25.37 | 0.0 | 0.0 | 0.0 | 25.37 |
1335+
| 2 | 28 | 01 March 2025 | | 50.28 | 24.93 | 0.44 | 0.0 | 0.0 | 25.37 | 0.0 | 0.0 | 0.0 | 25.37 |
1336+
| 3 | 31 | 01 April 2025 | | 25.2 | 25.08 | 0.29 | 0.0 | 0.0 | 25.37 | 0.0 | 0.0 | 0.0 | 25.37 |
1337+
| 4 | 30 | 01 May 2025 | | 0.0 | 25.2 | 0.15 | 0.0 | 0.0 | 25.35 | 0.0 | 0.0 | 0.0 | 25.35 |
1338+
And Loan Repayment schedule has the following data in Total row:
1339+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
1340+
| 100.0 | 1.46 | 0.0 | 0.0 | 101.46 | 0.0 | 0.0 | 0.0 | 101.46 |
1341+
And Loan Transactions tab has the following data:
1342+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
1343+
| 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
1344+
| 01 January 2025 | Contract Termination | 101.46 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | false | false |
1345+
1346+
@TestRailId:C4134
1347+
Scenario: Contract termination on disbursement date with interest recognition
1348+
When Admin sets the business date to "01 January 2025"
1349+
And Admin creates a client with random data
1350+
And Admin creates a fully customized loan with the following data:
1351+
| 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 |
1352+
| LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_CONTRACT_TERMINATION_INT_RECOGNITION | 01 January 2025 | 100 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 4 | MONTHS | 1 | MONTHS | 4 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
1353+
And Admin successfully approves the loan on "01 January 2025" with "100" amount and expected disbursement date on "01 January 2025"
1354+
And Admin successfully disburse the loan on "01 January 2025" with "100" EUR transaction amount
1355+
And Admin successfully terminates loan contract
1356+
Then Loan Repayment schedule has 4 periods, with the following data for periods:
1357+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
1358+
| | | 01 January 2025 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
1359+
| 1 | 31 | 01 February 2025 | | 75.21 | 24.79 | 0.58 | 0.0 | 0.0 | 25.37 | 0.0 | 0.0 | 0.0 | 25.37 |
1360+
| 2 | 28 | 01 March 2025 | | 50.28 | 24.93 | 0.44 | 0.0 | 0.0 | 25.37 | 0.0 | 0.0 | 0.0 | 25.37 |
1361+
| 3 | 31 | 01 April 2025 | | 25.2 | 25.08 | 0.29 | 0.0 | 0.0 | 25.37 | 0.0 | 0.0 | 0.0 | 25.37 |
1362+
| 4 | 30 | 01 May 2025 | | 0.0 | 25.2 | 0.15 | 0.0 | 0.0 | 25.35 | 0.0 | 0.0 | 0.0 | 25.35 |
1363+
And Loan Repayment schedule has the following data in Total row:
1364+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
1365+
| 100.0 | 1.46 | 0.0 | 0.0 | 101.46 | 0.0 | 0.0 | 0.0 | 101.46 |
1366+
And Loan Transactions tab has the following data:
1367+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
1368+
| 01 January 2025 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
1369+
| 01 January 2025 | Contract Termination | 101.46 | 100.0 | 1.46 | 0.0 | 0.0 | 0.0 | false | false |

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,13 +1411,14 @@ public void addLoanRepaymentScheduleInstallment(final LoanRepaymentScheduleInsta
14111411
* @return a schedule installment is related to the provided date
14121412
**/
14131413
public LoanRepaymentScheduleInstallment getRelatedRepaymentScheduleInstallment(final LocalDate date) {
1414-
return getRepaymentScheduleInstallment(e -> (DateUtils.isDateInRangeFromExclusiveToInclusive(date, e.getFromDate(), e.getDueDate())
1415-
|| (e.isFirstNormalInstallment(getRepaymentScheduleInstallments()) && DateUtils.isDateInRangeInclusive(date, e.getFromDate(), e.getDueDate()))));
1414+
return getRepaymentScheduleInstallment(e -> DateUtils.isDateInRangeFromExclusiveToInclusive(date, e.getFromDate(), e.getDueDate())
1415+
|| (e.isFirstNormalInstallment(getRepaymentScheduleInstallments()) && e.getFromDate().isEqual(date)));
14161416
}
14171417

14181418
public List<LoanRepaymentScheduleInstallment> getInstallmentsUpToTransactionDate(final LocalDate transactionDate) {
1419-
return getRepaymentScheduleInstallments().stream().filter(i -> (transactionDate.isAfter(i.getFromDate())
1420-
|| (i.isFirstNormalInstallment(getRepaymentScheduleInstallments())) && !transactionDate.isBefore(i.getFromDate())))
1419+
return getRepaymentScheduleInstallments().stream()
1420+
.filter(i -> transactionDate.isAfter(i.getFromDate())
1421+
|| (i.isFirstNormalInstallment(getRepaymentScheduleInstallments()) && i.getFromDate().isEqual(transactionDate)))
14211422
.collect(Collectors.toCollection(ArrayList::new));
14221423
}
14231424

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,6 @@ private BigDecimal setScaleAndDefaultToNullIfZero(final BigDecimal value) {
12171217
}
12181218

12191219
public boolean isFirstNormalInstallment(List<LoanRepaymentScheduleInstallment> installments) {
1220-
return installments.stream().filter(rp -> !rp.isDownPayment())
1221-
.findFirst().stream().anyMatch(rp -> rp.equals(this));
1220+
return installments.stream().filter(rp -> !rp.isDownPayment()).findFirst().stream().anyMatch(rp -> rp.equals(this));
12221221
}
12231222
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1902,7 +1902,8 @@ private void handleAccelerateMaturityDate(final LoanTransaction loanTransaction,
19021902
MathUtil.nullToZero(currentInstallment.getTotalPaidInAdvance()).add(futureTotalPaidInAdvance));
19031903
}
19041904

1905-
final List<LoanRepaymentScheduleInstallment> installmentsUpToTransactionDate = loan.getInstallmentsUpToTransactionDate(transactionDate);
1905+
final List<LoanRepaymentScheduleInstallment> installmentsUpToTransactionDate = loan
1906+
.getInstallmentsUpToTransactionDate(transactionDate);
19061907

19071908
final List<LoanTransaction> transactionsToBeReprocessed = loan.getLoanTransactions().stream()
19081909
.filter(transaction -> transaction.getTransactionDate().isBefore(transactionDate))

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ public BigDecimal getCalculatedDueInterest() {
144144
long lengthTillPeriodDueDate = getLengthTillPeriodDueDate();
145145
final BigDecimal interestDueTillRepaymentDueDate = getCalculatedDueInterest(
146146
getRepaymentPeriod().getLoanProductRelatedDetail().getInterestMethod(), lengthTillPeriodDueDate); //
147-
BigDecimal calculatedDueInterest = MathUtil.negativeToZero(MathUtil.add(getMc(), getCreditedInterest().getAmount(), interestDueTillRepaymentDueDate));
147+
BigDecimal calculatedDueInterest = MathUtil
148+
.negativeToZero(MathUtil.add(getMc(), getCreditedInterest().getAmount(), interestDueTillRepaymentDueDate));
148149
return calculatedDueInterest;
149150
}
150151

integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanContractTerminationTest.java

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -130,30 +130,32 @@ public void testLoanContractTerminationSameDisbursementDate() {
130130
final GlobalConfigurationHelper globalConfigurationHelper = new GlobalConfigurationHelper();
131131

132132
runAt("1 January 2024", () -> {
133-
/*
134-
PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive().interestRecognitionOnDisbursementDate(false));
135-
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
136-
500.0, 7.0, 6, (request) -> request.interestRecognitionOnDisbursementDate(false));
137-
138-
disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
139-
140-
loanTransactionHelper.moveLoanState(loanId,
141-
new PostLoansLoanIdRequest().note("Contract Termination Test").externalId(Utils.randomStringGenerator("", 20)),
142-
"contractTermination");
143-
144-
verifyTransactions(loanId, //
145-
transaction(100.0, "Disbursement", "01 January 2024"), //
146-
transaction(100.0, "Contract Termination", "01 January 2024"));
147-
148-
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
149-
assertEquals(BigDecimal.ZERO.stripTrailingZeros(),
150-
loanDetails.getSummary().getInterestCharged().stripTrailingZeros());
151-
*/
152-
153-
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.INTEREST_CHARGED_FROM_DATE_SAME_AS_DISBURSAL_DATE,
154-
new PutGlobalConfigurationsRequest().enabled(true));
155-
156-
PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive().interestRecognitionOnDisbursementDate(true));
133+
/*
134+
* PostLoanProductsResponse loanProductsResponse =
135+
* loanProductHelper.createLoanProduct(create4IProgressive().interestRecognitionOnDisbursementDate(false));
136+
* Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(),
137+
* "1 January 2024", 500.0, 7.0, 6, (request) -> request.interestRecognitionOnDisbursementDate(false));
138+
*
139+
* disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
140+
*
141+
* loanTransactionHelper.moveLoanState(loanId, new
142+
* PostLoansLoanIdRequest().note("Contract Termination Test").externalId(Utils.randomStringGenerator("",
143+
* 20)), "contractTermination");
144+
*
145+
* verifyTransactions(loanId, // transaction(100.0, "Disbursement", "01 January 2024"), //
146+
* transaction(100.0, "Contract Termination", "01 January 2024"));
147+
*
148+
* GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
149+
* assertEquals(BigDecimal.ZERO.stripTrailingZeros(),
150+
* loanDetails.getSummary().getInterestCharged().stripTrailingZeros());
151+
*/
152+
153+
globalConfigurationHelper.updateGlobalConfiguration(
154+
GlobalConfigurationConstants.INTEREST_CHARGED_FROM_DATE_SAME_AS_DISBURSAL_DATE,
155+
new PutGlobalConfigurationsRequest().enabled(true));
156+
157+
PostLoanProductsResponse loanProductsResponse = loanProductHelper
158+
.createLoanProduct(create4IProgressive().interestRecognitionOnDisbursementDate(true));
157159
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
158160
500.0, 7.0, 6, (request) -> request.interestRecognitionOnDisbursementDate(true));
159161

@@ -168,11 +170,11 @@ public void testLoanContractTerminationSameDisbursementDate() {
168170
transaction(204.11, "Contract Termination", "01 January 2024"));
169171

170172
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
171-
assertEquals(BigDecimal.valueOf(4.11).stripTrailingZeros(),
172-
loanDetails.getSummary().getInterestCharged().stripTrailingZeros());
173+
assertEquals(BigDecimal.valueOf(4.11).stripTrailingZeros(), loanDetails.getSummary().getInterestCharged().stripTrailingZeros());
173174

174-
globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.INTEREST_CHARGED_FROM_DATE_SAME_AS_DISBURSAL_DATE,
175-
new PutGlobalConfigurationsRequest().enabled(false));
175+
globalConfigurationHelper.updateGlobalConfiguration(
176+
GlobalConfigurationConstants.INTEREST_CHARGED_FROM_DATE_SAME_AS_DISBURSAL_DATE,
177+
new PutGlobalConfigurationsRequest().enabled(false));
176178
});
177179
}
178180

0 commit comments

Comments
 (0)