Skip to content

Commit f55b634

Browse files
committed
FINERACT-2326: Not implemented exception handling
1 parent c50ed19 commit f55b634

File tree

5 files changed

+120
-9
lines changed

5 files changed

+120
-9
lines changed

fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/ApiGlobalErrorResponse.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
2626
import static org.apache.http.HttpStatus.SC_METHOD_NOT_ALLOWED;
2727
import static org.apache.http.HttpStatus.SC_NOT_FOUND;
28+
import static org.apache.http.HttpStatus.SC_NOT_IMPLEMENTED;
2829
import static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;
2930
import static org.apache.http.HttpStatus.SC_UNAUTHORIZED;
3031

@@ -139,6 +140,15 @@ public static ApiGlobalErrorResponse domainRuleViolation(final String globalisat
139140
"Errors contain reason for domain rule violation.", errors);
140141
}
141142

143+
public static ApiGlobalErrorResponse notImplemented(final String globalisationMessageCode, final String defaultUserMessage,
144+
final Object... defaultUserMessageArgs) {
145+
final List<ApiParameterError> errors = new ArrayList<>();
146+
errors.add(ApiParameterError.generalError(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs));
147+
148+
return create(SC_NOT_IMPLEMENTED, "validation.msg.not.implemented", "Request was understood but not implemented.",
149+
"Errors contain reason for not implemented violation.", errors);
150+
}
151+
142152
public static ApiGlobalErrorResponse dataIntegrityError(final String globalisationMessageCode, final String defaultUserMessage,
143153
final String parameterName, final Object... defaultUserMessageArgs) {
144154
final List<ApiParameterError> errors = new ArrayList<>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.fineract.infrastructure.core.exceptionmapper;
20+
21+
import jakarta.ws.rs.core.MediaType;
22+
import jakarta.ws.rs.core.Response;
23+
import jakarta.ws.rs.core.Response.Status;
24+
import jakarta.ws.rs.ext.ExceptionMapper;
25+
import jakarta.ws.rs.ext.Provider;
26+
import lombok.extern.slf4j.Slf4j;
27+
import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
28+
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
29+
import org.springframework.context.annotation.Scope;
30+
import org.springframework.stereotype.Component;
31+
32+
/**
33+
* An {@link ExceptionMapper} to map {@link UnsupportedOperationException} thrown by platform into a HTTP API friendly
34+
* format.
35+
*
36+
* The {@link UnsupportedOperationException} is thrown when an operation was not yet implemented.
37+
*/
38+
@Provider
39+
@Component
40+
@Scope("singleton")
41+
@Slf4j
42+
public class UnsupporterOperationExceptionMapper implements FineractExceptionMapper, ExceptionMapper<UnsupportedOperationException> {
43+
44+
@Override
45+
public Response toResponse(final UnsupportedOperationException exception) {
46+
log.warn("Exception occurred", ErrorHandler.findMostSpecificException(exception));
47+
final ApiGlobalErrorResponse notFoundErrorResponse = ApiGlobalErrorResponse.notImplemented("not.implemented",
48+
exception.getMessage());
49+
return Response.status(Status.NOT_IMPLEMENTED).entity(notFoundErrorResponse).type(MediaType.APPLICATION_JSON).build();
50+
}
51+
52+
@Override
53+
public int errorCode() {
54+
return 5010;
55+
}
56+
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,4 +538,21 @@ PostLoansLoanIdTransactionsRequest setReAgeingRequestProperties(PostLoansLoanIdT
538538
}
539539
return request;
540540
}
541+
542+
@When("Admin creates a Loan re-aging transaction by Loan external ID with the following data, but fails with {int} error code:")
543+
public void adminCreatesALoanReAgingTransactionByLoanExternalIDWithTheFollowingDataButFailsWithErrorCode(int errorCode,
544+
DataTable table) {
545+
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
546+
String loanExternalId = loanResponse.getResourceExternalId();
547+
548+
PostLoansLoanIdTransactionsRequest reAgingRequest = setReAgeingRequestProperties(//
549+
LoanRequestFactory.defaultReAgingRequest(), //
550+
table.row(0), //
551+
table.row(1) //
552+
);
553+
554+
CallFailedRuntimeException response = fail(() -> fineractClient.loanTransactions().executeLoanTransaction1(loanExternalId,
555+
reAgingRequest, Map.<String, Object>of("command", "reAge")));
556+
assertThat(response.getStatus()).isEqualTo(errorCode);
557+
}
541558
}

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12320,7 +12320,6 @@ Feature: LoanReAging
1232012320
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
1232112321
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | true | false |
1232212322
| 15 March 2024 | Re-age | 85.08 | 83.57 | 1.51 | 0.0 | 0.0 | 0.0 | false | true |
12323-
1232412323
When Loan Pay-off is made on "01 April 2024"
1232512324
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
1232612325

@@ -14537,4 +14536,34 @@ Feature: LoanReAging
1453714536
When Loan Pay-off is made on "01 April 2024"
1453814537
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
1453914538

14539+
@TestRailId:C4272 @AdvancedPaymentAllocation
14540+
Scenario: Verify Re-aging on interest bearing, no interest recalculation loan is not allowed (not implemented)
14541+
When Admin sets the business date to "01 January 2024"
14542+
When Admin creates a client with random data
14543+
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
14544+
When Admin creates a fully customized loan with the following data:
14545+
| 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 |
14546+
| 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 |
14547+
And Admin successfully approves the loan on "01 January 2024" with "100" amount and expected disbursement date on "01 January 2024"
14548+
When Admin successfully disburse the loan on "01 January 2024" with "100" EUR transaction amount
14549+
Then Loan Repayment schedule has 6 periods, with the following data for periods:
14550+
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
14551+
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
14552+
| 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 |
14553+
| 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 |
14554+
| 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 |
14555+
| 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 |
14556+
| 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 |
14557+
| 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 |
14558+
Then Loan Repayment schedule has the following data in Total row:
14559+
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
14560+
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
14561+
Then Loan Transactions tab has the following data:
14562+
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
14563+
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
14564+
When Admin creates a Loan re-aging transaction by Loan external ID with the following data, but fails with 501 error code:
14565+
| frequencyNumber | frequencyType | startDate | numberOfInstallments |
14566+
| 1 | MONTHS | 16 January 2025 | 5 |
14567+
When Loan Pay-off is made on "01 January 2024"
14568+
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
1454014569

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
import lombok.NoArgsConstructor;
6363
import lombok.Setter;
6464
import lombok.extern.slf4j.Slf4j;
65-
import org.apache.commons.lang3.NotImplementedException;
6665
import org.apache.commons.lang3.ObjectUtils;
6766
import org.apache.commons.lang3.tuple.Pair;
6867
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@@ -165,34 +164,34 @@ public String getName() {
165164
protected Money handleTransactionThatIsALateRepaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
166165
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
167166
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
168-
throw new NotImplementedException();
167+
throw new UnsupportedOperationException();
169168
}
170169

171170
@Override
172171
protected Money handleTransactionThatIsPaymentInAdvanceOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
173172
List<LoanRepaymentScheduleInstallment> installments, LoanTransaction loanTransaction, Money paymentInAdvance,
174173
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
175-
throw new NotImplementedException();
174+
throw new UnsupportedOperationException();
176175
}
177176

178177
@Override
179178
protected Money handleTransactionThatIsOnTimePaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
180179
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
181180
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, Set<LoanCharge> charges) {
182-
throw new NotImplementedException();
181+
throw new UnsupportedOperationException();
183182
}
184183

185184
@Override
186185
protected Money handleRefundTransactionPaymentOfInstallment(LoanRepaymentScheduleInstallment currentInstallment,
187186
LoanTransaction loanTransaction, Money transactionAmountUnprocessed,
188187
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings) {
189-
throw new NotImplementedException();
188+
throw new UnsupportedOperationException();
190189
}
191190

192191
@Override
193192
public Money handleRepaymentSchedule(List<LoanTransaction> transactionsPostDisbursement, MonetaryCurrency currency,
194193
List<LoanRepaymentScheduleInstallment> installments, Set<LoanCharge> loanCharges) {
195-
throw new NotImplementedException();
194+
throw new UnsupportedOperationException();
196195
}
197196

198197
// only for progressive loans
@@ -2957,12 +2956,12 @@ private void handleReAge(LoanTransaction loanTransaction, TransactionCtx ctx) {
29572956
handleReAgeWithInterestRecalculationEnabled(loanTransaction, progressiveTransactionCtx);
29582957
} else if (loanTransaction.getLoan().isInterestBearing() && !loanTransaction.getLoan().isInterestRecalculationEnabled()) {
29592958
// TODO: implement interestRecalculation = false logic
2960-
throw new NotImplementedException(
2959+
throw new UnsupportedOperationException(
29612960
"Logic for re-aging when interest bearing loan has interestRecalculation disabled is not implemented");
29622961
}
29632962

29642963
} else if (LoanReAgeInterestHandlingType.WAIVE_INTEREST.equals(loanReAgeParameter.getInterestHandlingType())) {
2965-
throw new NotImplementedException("WAIVE_INTEREST interest handling strategy for re-aging is not implemented");
2964+
throw new UnsupportedOperationException("WAIVE_INTEREST interest handling strategy for re-aging is not implemented");
29662965
} else {
29672966
if (LoanReAgeInterestHandlingType.EQUAL_AMORTIZATION_FULL_INTEREST.equals(loanReAgeParameter.getInterestHandlingType())) {
29682967
CommonReAgeSettings settings = new CommonReAgeSettings(false, true, true, true);

0 commit comments

Comments
 (0)