You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2022/11/30 10:13:28 UTC

[fineract] branch develop updated: FINERACT-1760: Fix error handling

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 e95eba3e8 FINERACT-1760: Fix error handling
e95eba3e8 is described below

commit e95eba3e8a9374779022498cf65ce99df3841617
Author: Adam Saghy <ad...@gmail.com>
AuthorDate: Wed Nov 30 09:58:04 2022 +0100

    FINERACT-1760: Fix error handling
---
 .../exception/LoanChargeNotFoundException.java     |   7 +
 .../loanaccount/api/LoanChargesApiResource.java    |   8 ++
 .../api/LoanTransactionsApiResource.java           |   8 ++
 .../exception/LoanNotFoundException.java           |   9 +-
 .../LoanTransactionNotFoundException.java          |  10 +-
 .../ExternalIdSupportIntegrationTest.java          | 151 +++++++++++++++++++--
 .../common/loans/LoanTransactionHelper.java        |   7 +
 7 files changed, 188 insertions(+), 12 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/LoanChargeNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/LoanChargeNotFoundException.java
index 582c66d69..7ea034c07 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/LoanChargeNotFoundException.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/exception/LoanChargeNotFoundException.java
@@ -18,6 +18,8 @@
  */
 package org.apache.fineract.portfolio.charge.exception;
 
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
 import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
 
@@ -38,4 +40,9 @@ public class LoanChargeNotFoundException extends AbstractPlatformResourceNotFoun
         super("error.msg.loanCharge.id.invalid.for.given.loan", "Loan charge with identifier " + id + " does not exist for loan " + loanId,
                 id, loanId);
     }
+
+    public LoanChargeNotFoundException(ExternalId externalId) {
+        super("error.msg.loanCharge.external.id.invalid", "Loan Charge with external identifier "
+                + ObjectUtils.defaultIfNull(externalId, ExternalId.empty()).getValue() + " does not exist", externalId);
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
index cc5bdcd04..2778b3302 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanChargesApiResource.java
@@ -58,9 +58,11 @@ import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.charge.data.ChargeData;
 import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
+import org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundException;
 import org.apache.fineract.portfolio.charge.service.ChargeReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.data.LoanChargeData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanInstallmentChargeData;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.springframework.context.annotation.Scope;
@@ -549,6 +551,9 @@ public class LoanChargesApiResource {
         if (resolvedLoanChargeId == null) {
             loanChargeExternalId.throwExceptionIfEmpty();
             resolvedLoanChargeId = this.loanChargeReadPlatformService.retrieveLoanChargeIdByExternalId(loanChargeExternalId);
+            if (resolvedLoanChargeId == null) {
+                throw new LoanChargeNotFoundException(loanChargeExternalId);
+            }
         }
         return resolvedLoanChargeId;
     }
@@ -558,6 +563,9 @@ public class LoanChargesApiResource {
         if (resolvedLoanId == null) {
             loanExternalId.throwExceptionIfEmpty();
             resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId.getValue());
+            if (resolvedLoanId == null) {
+                throw new LoanNotFoundException(loanExternalId);
+            }
         }
         return resolvedLoanId;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
index 3d65f5697..7c3da1a92 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
@@ -62,6 +62,8 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
 import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
+import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
@@ -576,6 +578,9 @@ public class LoanTransactionsApiResource {
         if (resolvedLoanTransactionId == null) {
             externalTransactionId.throwExceptionIfEmpty();
             resolvedLoanTransactionId = this.loanReadPlatformService.retrieveLoanTransactionIdByExternalId(externalTransactionId);
+            if (resolvedLoanTransactionId == null) {
+                throw new LoanTransactionNotFoundException(externalTransactionId);
+            }
         }
         return resolvedLoanTransactionId;
     }
@@ -585,6 +590,9 @@ public class LoanTransactionsApiResource {
         if (resolvedLoanId == null) {
             loanExternalId.throwExceptionIfEmpty();
             resolvedLoanId = this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId.getValue());
+            if (resolvedLoanId == null) {
+                throw new LoanNotFoundException(loanExternalId);
+            }
         }
         return resolvedLoanId;
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanNotFoundException.java
index 9e8d17a43..73bb598a8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanNotFoundException.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanNotFoundException.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.portfolio.loanaccount.exception;
 
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
 
@@ -39,10 +40,14 @@ public class LoanNotFoundException extends AbstractPlatformResourceNotFoundExcep
     }
 
     public LoanNotFoundException(ExternalId externalId) {
-        super("error.msg.loan.external.id.invalid", "Loan with external identifier " + externalId + " does not exist", externalId);
+        super("error.msg.loan.external.id.invalid",
+                "Loan with external identifier " + ObjectUtils.defaultIfNull(externalId, ExternalId.empty()).getValue() + " does not exist",
+                externalId);
     }
 
     public LoanNotFoundException(ExternalId externalId, Exception e) {
-        super("error.msg.loan.external.id.invalid", "Loan with external identifier " + externalId + " does not exist", externalId, e);
+        super("error.msg.loan.external.id.invalid",
+                "Loan with external identifier " + ObjectUtils.defaultIfNull(externalId, ExternalId.empty()).getValue() + " does not exist",
+                externalId, e);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanTransactionNotFoundException.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanTransactionNotFoundException.java
index ee31742de..05ab128e0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanTransactionNotFoundException.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/exception/LoanTransactionNotFoundException.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.portfolio.loanaccount.exception;
 
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
 import org.springframework.dao.EmptyResultDataAccessException;
@@ -40,8 +41,15 @@ public class LoanTransactionNotFoundException extends AbstractPlatformResourceNo
         super("error.msg.loan.transaction.id.invalid", "Transaction with identifier " + id + " does not exist", id, e);
     }
 
+    public LoanTransactionNotFoundException(ExternalId transactionExternalId) {
+        super("error.msg.loan.transaction.external.id.invalid", "Transaction with external identifier "
+                + ObjectUtils.defaultIfNull(transactionExternalId, ExternalId.empty()).getValue() + " does not exist",
+                transactionExternalId);
+    }
+
     public LoanTransactionNotFoundException(ExternalId transactionExternalId, EmptyResultDataAccessException e) {
-        super("error.msg.loan.transaction.id.invalid", "Transaction with external identifier " + transactionExternalId + " does not exist",
+        super("error.msg.loan.transaction.external.id.invalid", "Transaction with external identifier "
+                + ObjectUtils.defaultIfNull(transactionExternalId, ExternalId.empty()).getValue() + " does not exist",
                 transactionExternalId, e);
     }
 }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
index b98065e48..f5c7b25b6 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalIdSupportIntegrationTest.java
@@ -20,6 +20,8 @@ package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.builder.ResponseSpecBuilder;
@@ -48,6 +50,7 @@ import org.apache.fineract.client.models.PutChargeTransactionChangesRequest;
 import org.apache.fineract.client.models.PutChargeTransactionChangesResponse;
 import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdRequest;
 import org.apache.fineract.client.models.PutLoansLoanIdChargesChargeIdResponse;
+import org.apache.fineract.client.util.CallFailedRuntimeException;
 import org.apache.fineract.integrationtests.client.IntegrationTest;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
@@ -157,15 +160,15 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
         assertNotNull(penalty2Result.getResourceExternalId());
 
         // Check whether we can fetch transaction templates with proper result http code (HTTP 200..300)
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "repayment", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "payoutRefund", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "waiveinterest", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "close-rescheduled", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "disburse", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "recoverypayment", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "refundbycash", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "foreclosure", null, null, null));
-        ok(fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, "creditBalanceRefund", null, null, null));
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "repayment", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "payoutRefund", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "waiveinterest", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "close-rescheduled", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "disburse", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "recoverypayment", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "refundbycash", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "foreclosure", null, null, null);
+        loanTransactionHelper.retrieveTransactionTemplate(loanExternalIdStr, "creditBalanceRefund", null, null, null);
 
         // Check whether an external id was generated
         String waiveChargeExternalIdStr = UUID.randomUUID().toString();
@@ -667,6 +670,136 @@ public class ExternalIdSupportIntegrationTest extends IntegrationTest {
         GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, 50, false);
     }
 
+    @Test
+    public void negativeTest() {
+        // INIT
+        final Account assetAccount = this.accountHelper.createAssetAccount();
+        final Account assetFeeAndPenaltyAccount = this.accountHelper.createAssetAccount();
+        final Account incomeAccount = this.accountHelper.createIncomeAccount();
+        final Account expenseAccount = this.accountHelper.createExpenseAccount();
+        final Account overpaymentAccount = this.accountHelper.createLiabilityAccount();
+
+        final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+                .withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+                .withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat()
+                .withAccountingRulePeriodicAccrual(new Account[] { assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
+                .withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0")
+                .withFeeAndPenaltyAssetAccount(assetFeeAndPenaltyAccount).build(null);
+        final Integer loanProductID = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+
+        final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
+
+        String loanExternalIdStr = UUID.randomUUID().toString();
+        final HashMap loan = applyForLoanApplication(client.getClientId().intValue(), loanProductID, loanExternalIdStr);
+        Integer loanId = (Integer) loan.get("resourceId");
+
+        this.loanTransactionHelper.approveLoan("02 September 2022", loanId);
+        String txnExternalIdStr = UUID.randomUUID().toString();
+        final HashMap disbursedLoanResult = this.loanTransactionHelper.disburseLoan("03 September 2022", loanId, "1000", txnExternalIdStr);
+
+        // Check whether the provided external id was retrieved
+        assertEquals(txnExternalIdStr, disbursedLoanResult.get("resourceExternalId"));
+
+        // NEGATIVE SCENARIOS
+
+        // GET
+        CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper
+                .retrieveTransactionTemplate("randomNonExistingLoanExternalId", "disburse", null, null, null));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper
+                .getLoanTransactionDetails("randomNonExistingLoanExternalId", "randomNonExistingLoanTransactionExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper
+                .getLoanTransactionDetails(loanExternalIdStr, "randomNonExistingLoanTransactionExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.transaction.external.id.invalid"));
+
+        // POST
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper
+                .makeChargeRefund("randomNonExistingLoanExternalId", new PostLoansLoanIdTransactionsRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.adjustLoanTransaction("randomNonExistingLoanExternalId",
+                        "randomNonExistingLoanTransactionExternalId", new PostLoansLoanIdTransactionsTransactionIdRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper.adjustLoanTransaction(loanExternalIdStr,
+                "randomNonExistingLoanTransactionExternalId", new PostLoansLoanIdTransactionsTransactionIdRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.transaction.external.id.invalid"));
+
+        // PUT
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.undoWaiveLoanCharge("randomNonExistingLoanExternalId",
+                        "randomNonExistingLoanTransactionExternalId", new PutChargeTransactionChangesRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper.undoWaiveLoanCharge(loanExternalIdStr,
+                "randomNonExistingLoanTransactionExternalId", new PutChargeTransactionChangesRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.transaction.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.getLoanCharges("randomNonExistingLoanExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.getLoanChargeTemplate("randomNonExistingLoanExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.getLoanCharge("randomNonExistingLoanExternalId", "randomNonExistingLoanChargeExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.getLoanCharge(loanExternalIdStr, "randomNonExistingLoanChargeExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loanCharge.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.payLoanCharge("randomNonExistingLoanExternalId", "randomNonExistingLoanChargeExternalId",
+                        new PostLoansLoanIdChargesChargeIdRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper.payLoanCharge(loanExternalIdStr,
+                "randomNonExistingLoanChargeExternalId", new PostLoansLoanIdChargesChargeIdRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loanCharge.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.updateLoanCharge("randomNonExistingLoanExternalId",
+                        "randomNonExistingLoanChargeExternalId", new PutLoansLoanIdChargesChargeIdRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper.updateLoanCharge(loanExternalIdStr,
+                "randomNonExistingLoanChargeExternalId", new PutLoansLoanIdChargesChargeIdRequest()));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loanCharge.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class, () -> this.loanTransactionHelper
+                .deleteLoanCharge("randomNonExistingLoanExternalId", "randomNonExistingLoanChargeExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loan.external.id.invalid"));
+
+        exception = assertThrows(CallFailedRuntimeException.class,
+                () -> this.loanTransactionHelper.deleteLoanCharge(loanExternalIdStr, "randomNonExistingLoanChargeExternalId"));
+        assertEquals(404, exception.getResponse().code());
+        assertTrue(exception.getMessage().contains("error.msg.loanCharge.external.id.invalid"));
+    }
+
     private HashMap applyForLoanApplication(final Integer clientID, final Integer loanProductID, final String externalId) {
         final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
                 .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1")
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 39edb4b97..1eba90212 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -52,6 +52,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
 import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdSummary;
+import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse;
 import org.apache.fineract.client.models.GetPaymentTypesResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdChargesChargeIdRequest;
@@ -1612,4 +1613,10 @@ public class LoanTransactionHelper extends IntegrationTest {
         return chargebackPayload;
     }
 
+    public GetLoansLoanIdTransactionsTemplateResponse retrieveTransactionTemplate(String loanExternalIdStr, String command,
+            String dateFormat, String transactionDate, String locale) {
+        return ok(
+                fineract().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr, command, dateFormat, transactionDate, locale));
+    }
+
 }