diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java index 41489897c..d1a6ede9f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java @@ -70,10 +70,8 @@ public enum LoanTransactionType { CAPITALIZED_INCOME(35, "loanTransactionType.capitalizedIncome"), // CAPITALIZED_INCOME_AMORTIZATION(36, "loanTransactionType.capitalizedIncomeAmortization"), // CAPITALIZED_INCOME_ADJUSTMENT(37, "loanTransactionType.capitalizedIncomeAdjustment"), // - CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT(39, "loanTransactionType.capitalizedIncomeAmortizationAdjustment"), // - // Kind of Final Transactions CONTRACT_TERMINATION(38, "loanTransactionType.contractTermination"), // - + CAPITALIZED_INCOME_AMORTIZATION_ADJUSTMENT(39, "loanTransactionType.capitalizedIncomeAmortizationAdjustment"), // BUY_DOWN_FEE(40, "loanTransactionType.buyDownFee"), // BUY_DOWN_FEE_ADJUSTMENT(41, "loanTransactionType.buyDownFeeAdjustment"), // BUY_DOWN_FEE_AMORTIZATION(42, "loanTransactionType.buyDownFeeAmortization"), // diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuyDownFeeBalance.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuyDownFeeBalance.java index ed6ce482e..679ee0ebc 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuyDownFeeBalance.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanBuyDownFeeBalance.java @@ -61,4 +61,5 @@ public class LoanBuyDownFeeBalance extends AbstractAuditableWithUTCDateTimeCusto @Column(name = "amount_adjustment", scale = 6, precision = 19) private BigDecimal amountAdjustment; + } 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 366d1d022..3d0ef8d18 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 @@ -689,7 +689,8 @@ public class LoanTransactionsApiResource { transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId, LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT, transactionId); } else if (CommandParameterUtil.is(commandParam, LoanApiConstants.BUY_DOWN_FEE_COMMAND)) { - transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId); + transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId, LoanTransactionType.BUY_DOWN_FEE, + transactionId); } else if (CommandParameterUtil.is(commandParam, LoanApiConstants.BUY_DOWN_FEE_ADJUSTMENT_COMMAND)) { transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId, LoanTransactionType.BUY_DOWN_FEE_ADJUSTMENT, transactionId); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java index 979bb4c06..c186d4281 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java @@ -33,7 +33,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanBuyDownFeeBalance; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; -import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeesBalanceRepository; +import org.apache.fineract.portfolio.loanaccount.repository.LoanBuyDownFeeBalanceRepository; import org.apache.fineract.portfolio.loanaccount.util.BuyDownFeeAmortizationUtil; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -44,7 +44,7 @@ import org.springframework.transaction.annotation.Transactional; public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyDownFeeAmortizationProcessingService { private final LoanTransactionRepository loanTransactionRepository; - private final LoanBuyDownFeesBalanceRepository loanBuyDownFeesBalanceRepository; + private final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository; private final BusinessEventNotifierService businessEventNotifierService; private final LoanJournalEntryPoster journalEntryPoster; private final ExternalIdFactory externalIdFactory; @@ -55,7 +55,7 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - List balances = loanBuyDownFeesBalanceRepository.findAllByLoanId(loan.getId()); + List balances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId()); LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() : getFinalBuyDownFeeAmortizationTransactionDate(loan); @@ -75,7 +75,7 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD .subtract(amortizationTillDate.getAmount())); } - loanBuyDownFeesBalanceRepository.saveAll(balances); + loanBuyDownFeeBalanceRepository.saveAll(balances); BigDecimal totalAmortized = loanTransactionRepository.getAmortizedAmountBuyDownFee(loan); BigDecimal totalAmortizationAmount = totalAmortization.getAmount().subtract(totalAmortized); 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 a409e0991..95113011c 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 @@ -484,16 +484,41 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa switch (transactionType) { case CAPITALIZED_INCOME: final Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); + BigDecimal capitalizedIncomeBalance = BigDecimal.ZERO; + BigDecimal buyDownFeeBalance = BigDecimal.ZERO; if (loan.getLoanProduct().getLoanProductRelatedDetail().isEnableIncomeCapitalization()) { - final BigDecimal capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream() + capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream() .map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); - transactionAmount = loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()).subtract(capitalizedIncomeBalance); } + // Calculate Buy Down Fee balance to subtract from Capitalized Income formula + buyDownFeeBalance = loanBuyDownFeeBalanceRepository.findAllByLoanId(loanId).stream().map(LoanBuyDownFeeBalance::getAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + transactionAmount = loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()).subtract(capitalizedIncomeBalance) + .subtract(buyDownFeeBalance); paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); loanTransactionData = LoanTransactionData.loanTransactionDataForCreditTemplate( LoanEnumerations.transactionType(transactionType), DateUtils.getBusinessLocalDate(), transactionAmount, paymentOptions, retriveLoanCurrencyData(loanId)); + break; + case BUY_DOWN_FEE: + final Loan buyDownLoan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); + BigDecimal buyDownFeeBalanceForCalc = BigDecimal.ZERO; + BigDecimal capitalizedIncomeBalanceForCalc = BigDecimal.ZERO; + // Calculate existing Buy Down Fee balance + buyDownFeeBalanceForCalc = loanBuyDownFeeBalanceRepository.findAllByLoanId(loanId).stream() + .map(LoanBuyDownFeeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + // Calculate Capitalized Income balance to subtract from Buy Down Fee formula + if (buyDownLoan.getLoanProduct().getLoanProductRelatedDetail().isEnableIncomeCapitalization()) { + capitalizedIncomeBalanceForCalc = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream() + .map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add); + } + transactionAmount = buyDownLoan.getApprovedPrincipal().subtract(buyDownLoan.getDisbursedAmount()) + .subtract(buyDownFeeBalanceForCalc).subtract(capitalizedIncomeBalanceForCalc); + paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes(); + loanTransactionData = LoanTransactionData.loanTransactionDataForCreditTemplate( + LoanEnumerations.transactionType(transactionType), DateUtils.getBusinessLocalDate(), transactionAmount, + paymentOptions, retriveLoanCurrencyData(loanId)); break; case CAPITALIZED_INCOME_ADJUSTMENT: final LoanCapitalizedIncomeBalance loanCapitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository 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 a891a2aeb..60358ea5f 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 @@ -567,10 +567,10 @@ public class LoanAccountConfiguration { @ConditionalOnMissingBean(LoanBuyDownFeeAmortizationProcessingService.class) public LoanBuyDownFeeAmortizationProcessingService loanBuyDownFeeAmortizationProcessingService( final LoanTransactionRepository loanTransactionRepository, - final LoanBuyDownFeesBalanceRepository loanBuyDownFeesBalanceRepository, + final LoanBuyDownFeeBalanceRepository loanBuyDownFeeBalanceRepository, final BusinessEventNotifierService businessEventNotifierService, final LoanJournalEntryPoster journalEntryPoster, final ExternalIdFactory externalIdFactory) { - return new LoanBuyDownFeeAmortizationProcessingServiceImpl(loanTransactionRepository, loanBuyDownFeesBalanceRepository, + return new LoanBuyDownFeeAmortizationProcessingServiceImpl(loanTransactionRepository, loanBuyDownFeeBalanceRepository, businessEventNotifierService, journalEntryPoster, externalIdFactory); } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java index 16db6e0e9..62e515239 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionTest.java @@ -208,4 +208,33 @@ public class LoanTransactionTest extends BaseLoanIntegrationTest { }); } + @Test + public void testGetLoanTransactionTemplateForBuyDownFee() { + final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); + + final PostLoanProductsResponse loanProductsResponse = loanProductHelper + .createLoanProduct(create4IProgressive().enableIncomeCapitalization(true) + .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT) + .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION) + .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()) + .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()) + .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)); + + final String loanExternalIdStr = UUID.randomUUID().toString(); + + runAt("20 December 2024", () -> { + Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "20 December 2024", + 430.0, 7.0, 6, (request) -> request.externalId(loanExternalIdStr)); + + disburseLoan(loanId, BigDecimal.valueOf(230), "20 December 2024"); + + final GetLoansLoanIdTransactionsTemplateResponse transactionTemplate = loanTransactionHelper.retrieveTransactionTemplate(loanId, + buyDownFeeCommand, null, null, null); + + assertNotNull(transactionTemplate); + assertEquals("loanTransactionType." + buyDownFeeCommand, transactionTemplate.getType().getCode()); + assertEquals(transactionTemplate.getAmount(), 200); + assertThat(transactionTemplate.getPaymentTypeOptions().size() > 0); + }); + } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java index bc4c92951..5970b5826 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/ExternalEventConfigurationHelper.java @@ -636,15 +636,15 @@ public class ExternalEventConfigurationHelper { loanTransactionUndoContractTerminationBusinessEvent.put("enabled", false); defaults.add(loanTransactionUndoContractTerminationBusinessEvent); - Map loanBuyDownFeeTransactionCreatedBusinessEvent = new HashMap<>(); - loanBuyDownFeeTransactionCreatedBusinessEvent.put("type", "LoanBuyDownFeeTransactionCreatedBusinessEvent"); - loanBuyDownFeeTransactionCreatedBusinessEvent.put("enabled", false); - defaults.add(loanBuyDownFeeTransactionCreatedBusinessEvent); + Map loanTransactionBuyDownFeePostBusinessEvent = new HashMap<>(); + loanTransactionBuyDownFeePostBusinessEvent.put("type", "LoanBuyDownFeeTransactionCreatedBusinessEvent"); + loanTransactionBuyDownFeePostBusinessEvent.put("enabled", false); + defaults.add(loanTransactionBuyDownFeePostBusinessEvent); - Map loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent = new HashMap<>(); - loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent.put("type", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent"); - loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent.put("enabled", false); - defaults.add(loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent); + Map loanTransactionBuyDownFeeAdjustmentPostBusinessEvent = new HashMap<>(); + loanTransactionBuyDownFeeAdjustmentPostBusinessEvent.put("type", "LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent"); + loanTransactionBuyDownFeeAdjustmentPostBusinessEvent.put("enabled", false); + defaults.add(loanTransactionBuyDownFeeAdjustmentPostBusinessEvent); Map loanBuyDownFeeAmortizationTransactionCreatedBusinessEvent = new HashMap<>(); loanBuyDownFeeAmortizationTransactionCreatedBusinessEvent.put("type", "LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent");