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

commit ca3f63badfbbb936f4a4ef55bb1a95abe3b6d37b
Author: Oleksii Novikov <[email protected]>
AuthorDate: Wed Jul 23 13:07:01 2025 +0300

    FINERACT-2311: Add GET API for buy down fees related information
---
 .../fineract/client/util/FineractClient.java       |  3 +
 .../loanaccount/domain/LoanRepository.java         |  4 +
 .../service/LoanReadPlatformService.java           |  3 +
 .../loanaccount/api/LoanBuyDownFeeApiResource.java | 87 ++++++++++++++++++++++
 ...ata.java => BuyDownFeeAmortizationDetails.java} | 17 +----
 .../loanaccount/data/LoanDeferredIncomeData.java   |  1 +
 .../LoanBuyDownFeeBalanceRepository.java           | 16 ----
 .../BuyDownFeeReadPlatformService.java}            | 18 ++---
 .../service/BuyDownFeeReadPlatformServiceImpl.java | 64 ++++++++++++++++
 .../loanaccount/api/LoanChargesApiResource.java    | 26 ++-----
 .../api/LoanTransactionsApiResource.java           | 24 ++----
 .../loanaccount/api/LoansApiResource.java          | 35 +++------
 .../pointintime/LoansPointInTimeApiDelegate.java   | 13 +---
 .../service/LoanReadPlatformServiceImpl.java       | 13 ++++
 .../starter/LoanAccountConfiguration.java          |  9 +++
 .../integrationtests/LoanBuyDownFeeTest.java       | 53 +++++++++++++
 .../common/loans/LoanTransactionHelper.java        |  5 ++
 17 files changed, 276 insertions(+), 115 deletions(-)

diff --git 
a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
 
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
index 307304dfd0..82d5a5e3ba 100644
--- 
a/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
+++ 
b/fineract-client/src/main/java/org/apache/fineract/client/util/FineractClient.java
@@ -85,6 +85,7 @@ import 
org.apache.fineract.client.services.InterestRateSlabAKAInterestBandsApi;
 import org.apache.fineract.client.services.JournalEntriesApi;
 import org.apache.fineract.client.services.ListReportMailingJobHistoryApi;
 import org.apache.fineract.client.services.LoanAccountLockApi;
+import org.apache.fineract.client.services.LoanBuyDownFeesApi;
 import org.apache.fineract.client.services.LoanChargesApi;
 import org.apache.fineract.client.services.LoanCobCatchUpApi;
 import org.apache.fineract.client.services.LoanCollateralApi;
@@ -304,6 +305,7 @@ public final class FineractClient {
     public final ExternalAssetOwnerLoanProductAttributesApi 
externalAssetOwnerLoanProductAttributes;
     public final LoanAccountLockApi loanAccountLockApi;
     public final InlineJobApi inlineJobApi;
+    public final LoanBuyDownFeesApi loanBuyDownFeesApi;
 
     private FineractClient(OkHttpClient okHttpClient, Retrofit retrofit) {
         this.okHttpClient = okHttpClient;
@@ -430,6 +432,7 @@ public final class FineractClient {
         loanInterestPauseApi = retrofit.create(LoanInterestPauseApi.class);
         progressiveLoanApi = retrofit.create(ProgressiveLoanApi.class);
         inlineJobApi = retrofit.create(InlineJobApi.class);
+        loanBuyDownFeesApi = retrofit.create(LoanBuyDownFeesApi.class);
     }
 
     public static Builder builder() {
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
index 05f3314d4b..24515f20ec 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanRepository.java
@@ -271,4 +271,8 @@ public interface LoanRepository extends JpaRepository<Loan, 
Long>, JpaSpecificat
 
     @Query("select loan.loanRepaymentScheduleDetail.enableIncomeCapitalization 
from Loan loan where loan.id = :loanId")
     Boolean isEnabledCapitalizedIncome(Long loanId);
+
+    @Query("select loan.loanRepaymentScheduleDetail.enableBuyDownFee from Loan 
loan where loan.id = :loanId")
+    Boolean isEnabledBuyDownFee(@Param("loanId") Long loanId);
+
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
index cf8c4eb08f..7abd182942 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -157,4 +157,7 @@ public interface LoanReadPlatformService {
     boolean existsByLoanId(Long loanId);
 
     LoanTransactionData retrieveManualInterestRefundTemplate(Long loanId, Long 
targetTransactionId);
+
+    Long getResolvedLoanId(ExternalId loanExternalId);
+
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanBuyDownFeeApiResource.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanBuyDownFeeApiResource.java
new file mode 100644
index 0000000000..915b2d732c
--- /dev/null
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanBuyDownFeeApiResource.java
@@ -0,0 +1,87 @@
+/**
+ * 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.portfolio.loanaccount.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.core.domain.ExternalId;
+import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
+import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import 
org.apache.fineract.portfolio.loanaccount.data.BuyDownFeeAmortizationDetails;
+import 
org.apache.fineract.portfolio.loanaccount.service.BuyDownFeeReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
+import org.springframework.stereotype.Component;
+
+@Path("/v1/loans")
+@Component
+@Tag(name = "Loan Buy Down Fees", description = "Loan Buy Down Fees")
+@RequiredArgsConstructor
+public class LoanBuyDownFeeApiResource {
+
+    private static final String RESOURCE_NAME_FOR_PERMISSIONS = "LOAN";
+    private final PlatformSecurityContext context;
+    private final BuyDownFeeReadPlatformService buyDownFeeReadPlatformService;
+    private final LoanReadPlatformService loanReadPlatformService;
+
+    @Path("/{loanId}/buydown-fees")
+    @GET
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Get the amortization details of Buy Down fees for a 
loan", description = "Returns a list of all Buy Down fee entries with 
amortization details for the specified loan.")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(array = @ArraySchema(schema = @Schema(implementation = 
BuyDownFeeAmortizationDetails.class)))) })
+    public List<BuyDownFeeAmortizationDetails> 
retrieveLoanBuyDownFeeAmortizationDetails(
+            @PathParam("loanId") @Parameter(description = "loanId", required = 
true) final Long loanId) {
+        
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
+        return 
this.buyDownFeeReadPlatformService.retrieveLoanBuyDownFeeAmortizationDetails(loanId);
+    }
+
+    @GET
+    @Path("/external-id/{loanExternalId}/buydown-fees")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Get the amortization details of Buy Down fees for a 
loan by external ID", description = "Returns a list of all Buy Down fee entries 
with amortization details for the loan specified by external ID.")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = 
@Content(array = @ArraySchema(schema = @Schema(implementation = 
BuyDownFeeAmortizationDetails.class)))) })
+    public List<BuyDownFeeAmortizationDetails> 
retrieveLoanBuyDownFeeAmortizationDetailsByExternalId(
+            @PathParam("loanExternalId") @Parameter(description = 
"loanExternalId", required = true) final String loanExternalId) {
+        
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
+
+        final ExternalId externalId = 
ExternalIdFactory.produce(loanExternalId);
+        final Long resolvedLoanId = 
loanReadPlatformService.getResolvedLoanId(externalId);
+
+        return 
this.buyDownFeeReadPlatformService.retrieveLoanBuyDownFeeAmortizationDetails(resolvedLoanId);
+    }
+
+}
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/BuyDownFeeAmortizationDetails.java
similarity index 72%
copy from 
fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
copy to 
fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/BuyDownFeeAmortizationDetails.java
index 393f1cf83e..191e73dda2 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/BuyDownFeeAmortizationDetails.java
@@ -18,19 +18,10 @@
  */
 package org.apache.fineract.portfolio.loanaccount.data;
 
-import java.util.List;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.ToString;
+import java.math.BigDecimal;
+import java.time.LocalDate;
 
-@AllArgsConstructor
-@NoArgsConstructor
-@ToString
-@Getter
-@Setter
-public class LoanDeferredIncomeData {
+public record BuyDownFeeAmortizationDetails(Long id, Long loanId, Long 
transactionId, LocalDate buyDownFeeDate, BigDecimal buyDownFeeAmount,
+        BigDecimal amortizedAmount, BigDecimal notYetAmortizedAmount, 
BigDecimal adjustedAmount, BigDecimal chargedOffAmount) {
 
-    private List<CapitalizedIncomeDetails> capitalizedIncomeData;
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
index 393f1cf83e..5c3a2f655e 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
@@ -33,4 +33,5 @@ import lombok.ToString;
 public class LoanDeferredIncomeData {
 
     private List<CapitalizedIncomeDetails> capitalizedIncomeData;
+
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanBuyDownFeeBalanceRepository.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanBuyDownFeeBalanceRepository.java
index 519e50fbc4..08fcb418b1 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanBuyDownFeeBalanceRepository.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanBuyDownFeeBalanceRepository.java
@@ -18,16 +18,11 @@
  */
 package org.apache.fineract.portfolio.loanaccount.repository;
 
-import java.math.BigDecimal;
 import java.util.List;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-import org.springframework.stereotype.Repository;
 
-@Repository
 public interface LoanBuyDownFeeBalanceRepository
         extends JpaRepository<LoanBuyDownFeeBalance, Long>, 
JpaSpecificationExecutor<LoanBuyDownFeeBalance> {
 
@@ -35,15 +30,4 @@ public interface LoanBuyDownFeeBalanceRepository
 
     LoanBuyDownFeeBalance findByLoanIdAndLoanTransactionId(Long loanId, Long 
transactionId);
 
-    @Query("SELECT lbfb FROM LoanBuyDownFeeBalance lbfb WHERE lbfb.loan.id = 
:loanId ORDER BY lbfb.date ASC")
-    List<LoanBuyDownFeeBalance> 
findRepaymentPeriodDataByLoanId(@Param("loanId") Long loanId);
-
-    @Query("SELECT COALESCE(SUM(lbfb.amount), 0) FROM LoanBuyDownFeeBalance 
lbfb WHERE lbfb.loan.id = :loanId")
-    BigDecimal calculateBuyDownFee(@Param("loanId") Long loanId);
-
-    @Query("SELECT COALESCE(SUM(lbfb.amountAdjustment), 0) FROM 
LoanBuyDownFeeBalance lbfb WHERE lbfb.loan.id = :loanId")
-    BigDecimal calculateBuyDownFeeAdjustment(@Param("loanId") Long loanId);
-
-    @Query("SELECT lbfb FROM LoanBuyDownFeeBalance lbfb WHERE lbfb.loan.id = 
:loanId AND lbfb.amountAdjustment IS NULL ORDER BY lbfb.date DESC")
-    List<LoanBuyDownFeeBalance> findBalanceForAdjustment(@Param("loanId") Long 
loanId);
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeReadPlatformService.java
similarity index 69%
copy from 
fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
copy to 
fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeReadPlatformService.java
index 393f1cf83e..e717082dff 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanDeferredIncomeData.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeReadPlatformService.java
@@ -16,21 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.loanaccount.data;
+package org.apache.fineract.portfolio.loanaccount.service;
 
 import java.util.List;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.ToString;
+import 
org.apache.fineract.portfolio.loanaccount.data.BuyDownFeeAmortizationDetails;
 
-@AllArgsConstructor
-@NoArgsConstructor
-@ToString
-@Getter
-@Setter
-public class LoanDeferredIncomeData {
+public interface BuyDownFeeReadPlatformService {
+
+    List<BuyDownFeeAmortizationDetails> 
retrieveLoanBuyDownFeeAmortizationDetails(Long loanId);
 
-    private List<CapitalizedIncomeDetails> capitalizedIncomeData;
 }
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeReadPlatformServiceImpl.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeReadPlatformServiceImpl.java
new file mode 100644
index 0000000000..7748abad29
--- /dev/null
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeReadPlatformServiceImpl.java
@@ -0,0 +1,64 @@
+/**
+ * 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.portfolio.loanaccount.service;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import 
org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
+import 
org.apache.fineract.portfolio.loanaccount.data.BuyDownFeeAmortizationDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import 
org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeeBalanceRepository;
+
+@RequiredArgsConstructor
+public class BuyDownFeeReadPlatformServiceImpl implements 
BuyDownFeeReadPlatformService {
+
+    private final LoanBuyDownFeeBalanceRepository 
loanBuyDownFeeBalanceRepository;
+    private final LoanRepository loanRepository;
+
+    @Override
+    public List<BuyDownFeeAmortizationDetails> 
retrieveLoanBuyDownFeeAmortizationDetails(final Long loanId) {
+        if (!loanRepository.existsById(loanId)) {
+            throw new 
GeneralPlatformDomainRuleException("error.msg.loan.is.not.found", "Loan: %s is 
not found".formatted(loanId), loanId);
+        }
+
+        if (!loanRepository.isEnabledBuyDownFee(loanId)) {
+            throw new 
GeneralPlatformDomainRuleException("error.msg.loan.is.not.enabled.buydown.fee",
+                    "Loan: %s is not enabled Buydown fee 
feature".formatted(loanId), loanId);
+        }
+        final List<LoanBuyDownFeeBalance> buyDownFeeBalances = 
loanBuyDownFeeBalanceRepository.findAllByLoanId(loanId);
+
+        return 
buyDownFeeBalances.stream().map(this::mapToLoanBuyDownFeeAmortizationData).collect(Collectors.toList());
+    }
+
+    private BuyDownFeeAmortizationDetails 
mapToLoanBuyDownFeeAmortizationData(final LoanBuyDownFeeBalance balance) {
+        final BigDecimal amortizedAmount = balance.getAmount() //
+                
.subtract(MathUtil.nullToZero(balance.getUnrecognizedAmount())) //
+                .subtract(MathUtil.nullToZero(balance.getAmountAdjustment())) 
//
+                .subtract(MathUtil.nullToZero(balance.getChargedOffAmount()));
+
+        return new BuyDownFeeAmortizationDetails(balance.getId(), 
balance.getLoan().getId(), balance.getLoanTransaction().getId(),
+                balance.getDate(), balance.getAmount(), amortizedAmount, 
balance.getUnrecognizedAmount(), balance.getAmountAdjustment(),
+                balance.getChargedOffAmount());
+    }
+
+}
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 e803a5756f..f50551e7c7 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
@@ -63,7 +63,6 @@ import 
org.apache.fineract.portfolio.charge.exception.LoanChargeNotFoundExceptio
 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.stereotype.Component;
@@ -421,7 +420,7 @@ public class LoanChargesApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId loanChargeExternalId = 
ExternalIdFactory.produce(loanChargeExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId, 
loanChargeExternalId);
 
         final CommandWrapper commandRequest = new 
CommandWrapperBuilder().deleteLoanCharge(resolvedLoanId, 
resolvedLoanChargeId).build();
@@ -438,7 +437,7 @@ public class LoanChargesApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId loanChargeExternalId = 
ExternalIdFactory.produce(loanChargeExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId, 
loanChargeExternalId);
 
         final LoanChargeData loanCharge = 
this.loanChargeReadPlatformService.retrieveLoanChargeDetails(resolvedLoanChargeId,
@@ -457,7 +456,7 @@ public class LoanChargesApiResource {
             final String apiRequestBodyAsJson) {
 
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
 
         CommandProcessingResult result;
         if (CommandParameterUtil.is(commandParam, COMMAND_PAY)) {
@@ -483,7 +482,7 @@ public class LoanChargesApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId loanChargeExternalId = 
ExternalIdFactory.produce(loanChargeExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId, 
loanChargeExternalId);
 
         final CommandWrapperBuilder builder = new 
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
@@ -513,7 +512,7 @@ public class LoanChargesApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId loanChargeExternalId = 
ExternalIdFactory.produce(loanChargeExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedLoanChargeId = getResolvedLoanChargeId(loanChargeId, 
loanChargeExternalId);
 
         final CommandWrapper commandRequest = new 
CommandWrapperBuilder().updateLoanCharge(resolvedLoanId, resolvedLoanChargeId)
@@ -528,7 +527,7 @@ public class LoanChargesApiResource {
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
 
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
 
         final Collection<LoanChargeData> loanCharges = 
this.loanChargeReadPlatformService.retrieveLoanCharges(resolvedLoanId);
 
@@ -540,7 +539,7 @@ public class LoanChargesApiResource {
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
 
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
 
         final List<ChargeData> chargeOptions = 
this.chargeReadPlatformService.retrieveLoanAccountApplicableCharges(resolvedLoanId,
                 new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
@@ -562,15 +561,4 @@ public class LoanChargesApiResource {
         return resolvedLoanChargeId;
     }
 
-    private Long getResolvedLoanId(final Long loanId, final ExternalId 
loanExternalId) {
-        Long resolvedLoanId = loanId;
-        if (resolvedLoanId == null) {
-            loanExternalId.throwExceptionIfEmpty();
-            resolvedLoanId = 
this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId);
-            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 b88965b999..22ed1e9110 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
@@ -91,8 +91,6 @@ public class LoanTransactionsApiResource {
     public static final String REAMORTIZE = "reAmortize";
     public static final String UNDO_REAMORTIZE = "undoReAmortize";
     public static final String CAPITALIZED_INCOME = "capitalizedIncome";
-    public static final String CAPITALIZED_INCOME_ADJUSTMENT = 
"capitalizedIncomeAdjustment";
-    public static final String CONTRACT_TERMINATION = "contractTermination";
     public static final String INTEREST_REFUND_COMMAND_VALUE = 
"interest-refund";
     private final Set<String> responseDataParameters = new 
HashSet<>(Arrays.asList("id", "type", "date", "currency", "amount", 
"externalId",
             LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME, 
LoanApiConstants.REVERSED_ON_DATE_PARAMNAME));
@@ -471,7 +469,7 @@ public class LoanTransactionsApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId transactionExternalId = 
ExternalIdFactory.produce(transactionExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedLoanTransactionId = 
getResolvedLoanTransactionId(transactionId, transactionExternalId);
 
         LoanTransactionData transactionData = 
this.loanReadPlatformService.retrieveLoanTransaction(resolvedLoanId,
@@ -552,7 +550,7 @@ public class LoanTransactionsApiResource {
         final CommandWrapperBuilder builder = new 
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
 
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
 
         CommandWrapper commandRequest = null;
         if (CommandParameterUtil.is(commandParam, "repayment")) {
@@ -618,7 +616,7 @@ public class LoanTransactionsApiResource {
 
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         LoanTransactionData transactionData;
 
         if (CommandParameterUtil.is(commandParam, "repayment")) {
@@ -714,7 +712,7 @@ public class LoanTransactionsApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId transactionExternalId = 
ExternalIdFactory.produce(transactionExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedTransactionId = 
getResolvedLoanTransactionId(transactionId, transactionExternalId);
         CommandWrapper commandRequest;
         if (CommandParameterUtil.is(commandParam, 
LoanApiConstants.CHARGEBACK_TRANSACTION_COMMAND)) {
@@ -739,7 +737,7 @@ public class LoanTransactionsApiResource {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
         ExternalId transactionExternalId = 
ExternalIdFactory.produce(transactionExternalIdStr);
 
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Long resolvedTransactionId = 
getResolvedLoanTransactionId(transactionId, transactionExternalId);
         final CommandWrapper commandRequest = new 
CommandWrapperBuilder().undoWaiveChargeTransaction(resolvedLoanId, 
resolvedTransactionId)
                 .build();
@@ -759,18 +757,6 @@ public class LoanTransactionsApiResource {
         return resolvedLoanTransactionId;
     }
 
-    private Long getResolvedLoanId(final Long loanId, final ExternalId 
loanExternalId) {
-        Long resolvedLoanId = loanId;
-        if (resolvedLoanId == null) {
-            loanExternalId.throwExceptionIfEmpty();
-            resolvedLoanId = 
this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId);
-            if (resolvedLoanId == null) {
-                throw new LoanNotFoundException(loanExternalId);
-            }
-        }
-        return resolvedLoanId;
-    }
-
     private Long getResolvedLoanIdWithExistsCheck(final Long loanId, final 
ExternalId loanExternalId) {
         if (loanId != null) {
             if (!loanReadPlatformService.existsByLoanId(loanId)) {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index f4f0e51b92..b1e74ea0c7 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -145,7 +145,6 @@ import 
org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryBalancesRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepaymentPeriodData;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.LoanTemplateTypeRequiredException;
 import 
org.apache.fineract.portfolio.loanaccount.exception.NotSupportedLoanTemplateTypeException;
 import org.apache.fineract.portfolio.loanaccount.guarantor.data.GuarantorData;
@@ -560,7 +559,6 @@ public class LoansApiResource {
         final CommandWrapper commandRequest = new 
CommandWrapperBuilder().createLoanApplication().withJson(apiRequestBodyAsJson).build();
 
         final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
-
         return this.toApiJsonSerializer.serialize(result);
     }
 
@@ -931,7 +929,7 @@ public class LoansApiResource {
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
         LoanApprovalData loanApprovalTemplate = null;
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         if (templateType == null) {
             final String errorMsg = "Loan template type must be provided";
             throw new LoanTemplateTypeRequiredException(errorMsg);
@@ -947,7 +945,7 @@ public class LoansApiResource {
             final UriInfo uriInfo) {
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         LoanAccountData loanBasicDetails = 
this.loanReadPlatformService.retrieveOne(resolvedLoanId);
         if (loanBasicDetails.isInterestRecalculationEnabled()) {
             Collection<CalendarData> interestRecalculationCalendarDatas = 
this.calendarReadPlatformService.retrieveCalendarsByEntity(
@@ -1241,7 +1239,7 @@ public class LoansApiResource {
         final CommandWrapperBuilder builder = new 
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
         CommandWrapper commandRequest;
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         if (CommandParameterUtil.is(commandParam, 
LoanApiConstants.MARK_AS_FRAUD_COMMAND)) {
             commandRequest = builder.markAsFraud(resolvedLoanId).build();
         } else {
@@ -1249,12 +1247,13 @@ public class LoansApiResource {
         }
 
         final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+
         return this.toApiJsonSerializer.serialize(result);
     }
 
     private String deleteLoanApplication(final Long loanId, final String 
loanExternalIdStr) {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         final CommandWrapper commandRequest = new 
CommandWrapperBuilder().deleteLoanApplication(resolvedLoanId).build();
         final CommandProcessingResult result = 
this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
         return this.toApiJsonSerializer.serialize(result);
@@ -1263,7 +1262,7 @@ public class LoansApiResource {
     private String stateTransitions(final Long loanId, final String 
loanExternalIdStr, final String commandParam,
             final String apiRequestBodyAsJson) {
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         final CommandWrapperBuilder builder = new 
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
         CommandWrapper commandRequest = null;
         if (CommandParameterUtil.is(commandParam, "reject")) {
@@ -1309,28 +1308,16 @@ public class LoansApiResource {
     private String getDelinquencyTagHistory(final Long loanId, final String 
loanExternalIdStr, final UriInfo uriInfo) {
         
context.authenticatedUser().validateHasReadPermission("DELINQUENCY_TAGS");
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         final Collection<LoanDelinquencyTagHistoryData> 
loanDelinquencyTagHistoryData = this.delinquencyReadPlatformService
                 .retrieveDelinquencyRangeHistory(resolvedLoanId);
         return 
this.jsonSerializerTagHistory.serialize(loanDelinquencyTagHistoryData);
     }
 
-    private Long getResolvedLoanId(final Long loanId, final ExternalId 
loanExternalId) {
-        Long resolvedLoanId = loanId;
-        if (resolvedLoanId == null) {
-            loanExternalId.throwExceptionIfEmpty();
-            resolvedLoanId = 
this.loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId);
-            if (resolvedLoanId == null) {
-                throw new LoanNotFoundException(loanExternalId);
-            }
-        }
-        return resolvedLoanId;
-    }
-
     private String getLoanDelinquencyActions(final Long loanId, final String 
loanExternalIdStr, final UriInfo uriInfo) {
         
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
 
         final Collection<LoanDelinquencyAction> delinquencyActions = 
this.delinquencyReadPlatformService
                 .retrieveLoanDelinquencyActions(resolvedLoanId);
@@ -1340,7 +1327,7 @@ public class LoansApiResource {
 
     private String createLoanDelinquencyAction(Long loanId, ExternalId 
loanExternalId, String apiRequestBodyAsJson) {
         
context.authenticatedUser().validateHasCreatePermission(RESOURCE_NAME_FOR_DELINQUENCY_ACTION_PERMISSIONS);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
 
         CommandWrapperBuilder builder = new 
CommandWrapperBuilder().createDelinquencyAction(resolvedLoanId);
         builder.withJson(apiRequestBodyAsJson);
@@ -1349,7 +1336,7 @@ public class LoansApiResource {
     }
 
     private CommandProcessingResult modifyLoanApprovedAmount(Long loanId, 
ExternalId loanExternalId, String apiRequestBodyAsJson) {
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         final CommandWrapperBuilder builder = new 
CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
         CommandWrapper commandRequest = 
builder.updateLoanApprovedAmount(resolvedLoanId).build();
 
@@ -1358,7 +1345,7 @@ public class LoansApiResource {
 
     private List<LoanApprovedAmountHistoryData> 
getLoanApprovedAmountHistory(Long loanId, ExternalId loanExternalId) {
         
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
-        Long resolvedLoanId = getResolvedLoanId(loanId, loanExternalId);
+        Long resolvedLoanId = loanId == null ? 
loanReadPlatformService.getResolvedLoanId(loanExternalId) : loanId;
         Pageable sortedByCreationDate = 
Pageable.unpaged(Sort.by("createdDate").ascending());
         return 
loanApprovedAmountHistoryRepository.findAllByLoanId(resolvedLoanId, 
sortedByCreationDate);
     }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/pointintime/LoansPointInTimeApiDelegate.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/pointintime/LoansPointInTimeApiDelegate.java
index 0eb3616bd2..07cf5fa8bc 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/pointintime/LoansPointInTimeApiDelegate.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/pointintime/LoansPointInTimeApiDelegate.java
@@ -30,7 +30,6 @@ import 
org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
 import 
org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeExternalIdsRequest;
 import 
org.apache.fineract.portfolio.loanaccount.api.pointintime.data.RetrieveLoansPointInTimeRequest;
 import org.apache.fineract.portfolio.loanaccount.data.LoanPointInTimeData;
-import 
org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanPointInTimeService;
 import 
org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.springframework.stereotype.Component;
@@ -56,9 +55,9 @@ public class LoansPointInTimeApiDelegate implements 
LoansPointInTimeApi {
             String locale) {
         
context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
-        Long loanId = resolveExternalId(loanExternalId);
+        Long resolvedLoanId = 
loanReadPlatformService.getResolvedLoanId(loanExternalId);
 
-        return getLoanPointInTime(loanId, dateParam, dateFormat, locale);
+        return getLoanPointInTime(resolvedLoanId, dateParam, dateFormat, 
locale);
     }
 
     @Override
@@ -103,12 +102,4 @@ public class LoansPointInTimeApiDelegate implements 
LoansPointInTimeApi {
         return 
loanReadPlatformService.retrieveLoanIdsByExternalIds(loanExternalIds);
     }
 
-    private Long resolveExternalId(ExternalId loanExternalId) {
-        loanExternalId.throwExceptionIfEmpty();
-        Long resolvedLoanId = 
loanReadPlatformService.retrieveLoanIdByExternalId(loanExternalId);
-        if (resolvedLoanId == null) {
-            throw new LoanNotFoundException(loanExternalId);
-        }
-        return resolvedLoanId;
-    }
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 93a73ad42c..e3f4009164 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -2469,6 +2469,19 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                 interestRefundAmount, paymentTypeOptions, 
loan.getCurrency().toData());
     }
 
+    @Override
+    public Long getResolvedLoanId(final ExternalId loanExternalId) {
+        loanExternalId.throwExceptionIfEmpty();
+
+        final Long resolvedLoanId = retrieveLoanIdByExternalId(loanExternalId);
+
+        if (resolvedLoanId == null) {
+            throw new LoanNotFoundException(loanExternalId);
+        }
+
+        return resolvedLoanId;
+    }
+
     private LoanTransaction deriveDefaultInterestWaiverTransaction(final Loan 
loan) {
         final Money totalInterestOutstanding = 
loan.getTotalInterestOutstandingOnLoan();
         Money possibleInterestToWaive = totalInterestOutstanding.copy();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
index 9c50ac7da3..2889c2028f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java
@@ -105,6 +105,8 @@ import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanUpdateCommand
 import 
org.apache.fineract.portfolio.loanaccount.service.BulkLoansReadPlatformService;
 import 
org.apache.fineract.portfolio.loanaccount.service.BulkLoansReadPlatformServiceImpl;
 import 
org.apache.fineract.portfolio.loanaccount.service.BuyDownFeePlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.BuyDownFeeReadPlatformService;
+import 
org.apache.fineract.portfolio.loanaccount.service.BuyDownFeeReadPlatformServiceImpl;
 import 
org.apache.fineract.portfolio.loanaccount.service.BuyDownFeeWritePlatformServiceImpl;
 import 
org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformService;
 import 
org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlatformServiceImpl;
@@ -403,6 +405,13 @@ public class LoanAccountConfiguration {
                 businessEventNotifierService, 
loanTransactionRelationRepository);
     }
 
+    @Bean
+    @ConditionalOnMissingBean(BuyDownFeeReadPlatformService.class)
+    public BuyDownFeeReadPlatformService buyDownFeeReadPlatformService(
+            final LoanBuyDownFeeBalanceRepository 
loanBuyDownFeeBalanceRepository, final LoanRepository loanRepository) {
+        return new 
BuyDownFeeReadPlatformServiceImpl(loanBuyDownFeeBalanceRepository, 
loanRepository);
+    }
+
     @Bean
     @ConditionalOnMissingBean(LoanWritePlatformService.class)
     public LoanWritePlatformService 
loanWritePlatformService(PlatformSecurityContext context,
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java
index 44dcf68454..ad3ecbd9fe 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanBuyDownFeeTest.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -31,6 +32,7 @@ import java.util.Optional;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.BuyDownFeeAmortizationDetails;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
 import org.apache.fineract.client.models.PostClientsResponse;
@@ -576,6 +578,57 @@ public class LoanBuyDownFeeTest extends 
BaseLoanIntegrationTest {
         });
     }
 
+    @Test
+    public void testRetrieveBuyDownFeeAmortizationDetails() {
+        final PostClientsResponse client = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
+        final PostLoanProductsResponse loanProduct = 
loanProductHelper.createLoanProduct(createProgressiveLoanProductWithBuyDownFee());
+
+        final long loanId = 
applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 February 2024", 1000.0,
+                7.0, 6, null);
+
+        disburseLoan(loanId, BigDecimal.valueOf(1000), "1 February 2024");
+
+        addBuyDownFeeForLoan(loanId, 100.0, "1 February 2024");
+
+        final List<BuyDownFeeAmortizationDetails> amortizationDetails = 
loanTransactionHelper.fetchBuyDownFeeAmortizationDetails(loanId);
+
+        assertNotNull(amortizationDetails);
+        assertFalse(amortizationDetails.isEmpty());
+
+        final BuyDownFeeAmortizationDetails amortizationDetail = 
amortizationDetails.getFirst();
+        assertNotNull(amortizationDetail);
+        assertNotNull(amortizationDetail.getId());
+        assertEquals(loanId, amortizationDetail.getLoanId());
+        assertNotNull(amortizationDetail.getTransactionId());
+        assertEquals(LocalDate.of(2024, 2, 1), 
amortizationDetail.getBuyDownFeeDate());
+        assertNotNull(amortizationDetail.getBuyDownFeeAmount());
+        assertEquals(0, 
BigDecimal.valueOf(100.0).compareTo(amortizationDetail.getBuyDownFeeAmount()));
+        assertNotNull(amortizationDetail.getAmortizedAmount());
+        assertEquals(0, amortizationDetail.getAmortizedAmount().signum());
+        assertNotNull(amortizationDetail.getNotYetAmortizedAmount());
+        assertEquals(0, 
BigDecimal.valueOf(100.0).compareTo(amortizationDetail.getNotYetAmortizedAmount()));
+    }
+
+    @Test
+    public void testRetrieveBuyDownFeeAmortizationDetails_notEnabled() {
+        final PostClientsResponse client = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
+        final PostLoanProductsResponse loanProduct = loanProductHelper
+                
.createLoanProduct(createProgressiveLoanProductWithBuyDownFee().enableBuyDownFee(false));
+
+        final long loanId = 
applyAndApproveProgressiveLoan(client.getClientId(), 
loanProduct.getResourceId(), "1 February 2024", 1000.0,
+                7.0, 6, null);
+
+        disburseLoan(loanId, BigDecimal.valueOf(1000), "1 February 2024");
+
+        final CallFailedRuntimeException exception = 
assertThrows(CallFailedRuntimeException.class, () -> {
+            addBuyDownFeeForLoan(loanId, 100.0, "1 February 2024");
+        });
+
+        assertEquals(400, exception.getResponse().code());
+        
assertTrue(exception.getMessage().contains("buy.down.fee.not.enabled"));
+        assertTrue(exception.getMessage().contains("Buy down fee is not 
enabled for this loan product"));
+    }
+
     @Test
     public void testReverseBuyDownFeeTransaction() {
         final AtomicReference<Long> buyDownFeeTransactionIdIdRef = new 
AtomicReference<>();
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 d26ce54584..74359dddfd 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
@@ -45,6 +45,7 @@ import java.util.Locale;
 import java.util.Map;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.AdvancedPaymentData;
+import org.apache.fineract.client.models.BuyDownFeeAmortizationDetails;
 import org.apache.fineract.client.models.CommandProcessingResult;
 import 
org.apache.fineract.client.models.DeleteLoansLoanIdChargesChargeIdResponse;
 import org.apache.fineract.client.models.DeleteLoansLoanIdResponse;
@@ -3177,4 +3178,8 @@ public class LoanTransactionHelper {
                 .build(clientID.toString(), loanProductID.toString(), 
savingsId);
         return getLoanId(loanApplicationJSON);
     }
+
+    public List<BuyDownFeeAmortizationDetails> 
fetchBuyDownFeeAmortizationDetails(Long loanId) {
+        return 
Calls.ok(FineractClientHelper.getFineractClient().loanBuyDownFeesApi.retrieveLoanBuyDownFeeAmortizationDetails(loanId));
+    }
 }

Reply via email to