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);

Reply via email to