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