This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new f55b63432e FINERACT-2326: Not implemented exception handling
f55b63432e is described below
commit f55b63432e1677bc1b3250861f602c69294db92a
Author: Adam Saghy <[email protected]>
AuthorDate: Tue Dec 2 16:51:42 2025 +0100
FINERACT-2326: Not implemented exception handling
---
.../core/data/ApiGlobalErrorResponse.java | 10 ++++
.../UnsupporterOperationExceptionMapper.java | 56 ++++++++++++++++++++++
.../test/stepdef/loan/LoanReAgingStepDef.java | 17 +++++++
.../test/resources/features/LoanReAging.feature | 31 +++++++++++-
...dvancedPaymentScheduleTransactionProcessor.java | 15 +++---
5 files changed, 120 insertions(+), 9 deletions(-)
diff --git
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
index b71d498266..22cb944df6 100644
---
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java
@@ -25,6 +25,7 @@ import static org.apache.http.HttpStatus.SC_FORBIDDEN;
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
+import static org.apache.http.HttpStatus.SC_NOT_IMPLEMENTED;
import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;
import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
@@ -139,6 +140,15 @@ public class ApiGlobalErrorResponse {
"Errors contain reason for domain rule violation.", errors);
}
+ public static ApiGlobalErrorResponse notImplemented(final String
globalisationMessageCode, final String defaultUserMessage,
+ final Object... defaultUserMessageArgs) {
+ final List<ApiParameterError> errors = new ArrayList<>();
+ errors.add(ApiParameterError.generalError(globalisationMessageCode,
defaultUserMessage, defaultUserMessageArgs));
+
+ return create(SC_NOT_IMPLEMENTED, "validation.msg.not.implemented",
"Request was understood but not implemented.",
+ "Errors contain reason for not implemented violation.",
errors);
+ }
+
public static ApiGlobalErrorResponse dataIntegrityError(final String
globalisationMessageCode, final String defaultUserMessage,
final String parameterName, final Object...
defaultUserMessageArgs) {
final List<ApiParameterError> errors = new ArrayList<>();
diff --git
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/UnsupporterOperationExceptionMapper.java
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/UnsupporterOperationExceptionMapper.java
new file mode 100644
index 0000000000..ce79b61cb1
--- /dev/null
+++
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/UnsupporterOperationExceptionMapper.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.core.exceptionmapper;
+
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.Response.Status;
+import jakarta.ws.rs.ext.ExceptionMapper;
+import jakarta.ws.rs.ext.Provider;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
+import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+/**
+ * An {@link ExceptionMapper} to map {@link UnsupportedOperationException}
thrown by platform into a HTTP API friendly
+ * format.
+ *
+ * The {@link UnsupportedOperationException} is thrown when an operation was
not yet implemented.
+ */
+@Provider
+@Component
+@Scope("singleton")
+@Slf4j
+public class UnsupporterOperationExceptionMapper implements
FineractExceptionMapper, ExceptionMapper<UnsupportedOperationException> {
+
+ @Override
+ public Response toResponse(final UnsupportedOperationException exception) {
+ log.warn("Exception occurred",
ErrorHandler.findMostSpecificException(exception));
+ final ApiGlobalErrorResponse notFoundErrorResponse =
ApiGlobalErrorResponse.notImplemented("not.implemented",
+ exception.getMessage());
+ return
Response.status(Status.NOT_IMPLEMENTED).entity(notFoundErrorResponse).type(MediaType.APPLICATION_JSON).build();
+ }
+
+ @Override
+ public int errorCode() {
+ return 5010;
+ }
+}
diff --git
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAgingStepDef.java
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAgingStepDef.java
index 52283d322e..abc8621a3e 100644
---
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAgingStepDef.java
+++
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanReAgingStepDef.java
@@ -538,4 +538,21 @@ public class LoanReAgingStepDef extends AbstractStepDef {
}
return request;
}
+
+ @When("Admin creates a Loan re-aging transaction by Loan external ID with
the following data, but fails with {int} error code:")
+ public void
adminCreatesALoanReAgingTransactionByLoanExternalIDWithTheFollowingDataButFailsWithErrorCode(int
errorCode,
+ DataTable table) {
+ PostLoansResponse loanResponse =
testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ String loanExternalId = loanResponse.getResourceExternalId();
+
+ PostLoansLoanIdTransactionsRequest reAgingRequest =
setReAgeingRequestProperties(//
+ LoanRequestFactory.defaultReAgingRequest(), //
+ table.row(0), //
+ table.row(1) //
+ );
+
+ CallFailedRuntimeException response = fail(() ->
fineractClient.loanTransactions().executeLoanTransaction1(loanExternalId,
+ reAgingRequest, Map.<String, Object>of("command", "reAge")));
+ assertThat(response.getStatus()).isEqualTo(errorCode);
+ }
}
diff --git
a/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature
b/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature
index 1e23586774..687b3a58ba 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature
@@ -12320,7 +12320,6 @@ Feature: LoanReAging
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 |
0.0 | 0.0 | 83.57 | false | false |
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 |
0.0 | 0.0 | 67.05 | true | false |
| 15 March 2024 | Re-age | 85.08 | 83.57 | 1.51 |
0.0 | 0.0 | 0.0 | false | true |
-
When Loan Pay-off is made on "01 April 2024"
Then Loan is closed with zero outstanding balance and it's all
installments have obligations met
@@ -14537,4 +14536,34 @@ Feature: LoanReAging
When Loan Pay-off is made on "01 April 2024"
Then Loan is closed with zero outstanding balance and it's all
installments have obligations met
+ @TestRailId:C4272 @AdvancedPaymentAllocation
+ Scenario: Verify Re-aging on interest bearing, no interest recalculation
loan is not allowed (not implemented)
+ When Admin sets the business date to "01 January 2024"
+ When Admin creates a client with random data
+ When Admin set
"LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_CHARGEBACK_PRINCIPAL_INTEREST_FEE"
loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future
installment allocation rule
+ When Admin creates a fully customized loan with the following data:
+ | 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 |
+ |
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_CHARGEBACK_PRINCIPAL_INTEREST_FEE | 01
January 2024 | 100 | 7 | DECLINING_BALANCE |
DAILY | EQUAL_INSTALLMENTS | 6 | MONTHS
| 1 | MONTHS | 6 | 0
| 0 | 0 |
ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 January 2024" with "100"
amount and expected disbursement date on "01 January 2024"
+ When Admin successfully disburse the loan on "01 January 2024" with "100"
EUR transaction amount
+ Then Loan Repayment schedule has 6 periods, with the following data for
periods:
+ | Nr | Days | Date | Paid date | Balance of loan | Principal
due | Interest | Fees | Penalties | Due | Paid | In advance | Late |
Outstanding |
+ | | | 01 January 2024 | | 100.0 |
| | 0.0 | | 0.0 | 0.0 | | |
|
+ | 1 | 31 | 01 February 2024| | 83.57 | 16.43
| 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01
|
+ | 2 | 29 | 01 March 2024 | | 67.05 | 16.52
| 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01
|
+ | 3 | 31 | 01 April 2024 | | 50.43 | 16.62
| 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01
|
+ | 4 | 30 | 01 May 2024 | | 33.71 | 16.72
| 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01
|
+ | 5 | 31 | 01 June 2024 | | 16.9 | 16.81
| 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01
|
+ | 6 | 30 | 01 July 2024 | | 0.0 | 16.9
| 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0
|
+ Then Loan Repayment schedule has the following data in Total row:
+ | Principal due | Interest | Fees | Penalties | Due | Paid | In
advance | Late | Outstanding |
+ | 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0
| 0.0 | 102.05 |
+ Then Loan Transactions tab has the following data:
+ | Transaction date | Transaction Type | Amount | Principal | Interest |
Fees | Penalties | Loan Balance | Reverted |
+ | 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 |
0.0 | 0.0 | 100.0 | false |
+ When Admin creates a Loan re-aging transaction by Loan external ID with
the following data, but fails with 501 error code:
+ | frequencyNumber | frequencyType | startDate |
numberOfInstallments |
+ | 1 | MONTHS | 16 January 2025 | 5
|
+ When Loan Pay-off is made on "01 January 2024"
+ Then Loan is closed with zero outstanding balance and it's all
installments have obligations met
diff --git
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index b0965cf55b..5564950368 100644
---
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -62,7 +62,6 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.tuple.Pair;
import
org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@@ -165,34 +164,34 @@ public class AdvancedPaymentScheduleTransactionProcessor
extends AbstractLoanRep
protected Money
handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment
currentInstallment,
List<LoanRepaymentScheduleInstallment> installments,
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping>
transactionMappings, Set<LoanCharge> charges) {
- throw new NotImplementedException();
+ throw new UnsupportedOperationException();
}
@Override
protected Money
handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment
currentInstallment,
List<LoanRepaymentScheduleInstallment> installments,
LoanTransaction loanTransaction, Money paymentInAdvance,
List<LoanTransactionToRepaymentScheduleMapping>
transactionMappings, Set<LoanCharge> charges) {
- throw new NotImplementedException();
+ throw new UnsupportedOperationException();
}
@Override
protected Money
handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment
currentInstallment,
LoanTransaction loanTransaction, Money
transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping>
transactionMappings, Set<LoanCharge> charges) {
- throw new NotImplementedException();
+ throw new UnsupportedOperationException();
}
@Override
protected Money
handleRefundTransactionPaymentOfInstallment(LoanRepaymentScheduleInstallment
currentInstallment,
LoanTransaction loanTransaction, Money
transactionAmountUnprocessed,
List<LoanTransactionToRepaymentScheduleMapping>
transactionMappings) {
- throw new NotImplementedException();
+ throw new UnsupportedOperationException();
}
@Override
public Money handleRepaymentSchedule(List<LoanTransaction>
transactionsPostDisbursement, MonetaryCurrency currency,
List<LoanRepaymentScheduleInstallment> installments,
Set<LoanCharge> loanCharges) {
- throw new NotImplementedException();
+ throw new UnsupportedOperationException();
}
// only for progressive loans
@@ -2957,12 +2956,12 @@ public class
AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
handleReAgeWithInterestRecalculationEnabled(loanTransaction,
progressiveTransactionCtx);
} else if (loanTransaction.getLoan().isInterestBearing() &&
!loanTransaction.getLoan().isInterestRecalculationEnabled()) {
// TODO: implement interestRecalculation = false logic
- throw new NotImplementedException(
+ throw new UnsupportedOperationException(
"Logic for re-aging when interest bearing loan has
interestRecalculation disabled is not implemented");
}
} else if
(LoanReAgeInterestHandlingType.WAIVE_INTEREST.equals(loanReAgeParameter.getInterestHandlingType()))
{
- throw new NotImplementedException("WAIVE_INTEREST interest
handling strategy for re-aging is not implemented");
+ throw new UnsupportedOperationException("WAIVE_INTEREST
interest handling strategy for re-aging is not implemented");
} else {
if
(LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_FULL_INTEREST.equals(loanReAgeParameter.getInterestHandlingType()))
{
CommonReAgeSettings settings = new
CommonReAgeSettings(false, true, true, true);