This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 53108227ae FINERACT-2311: Add Buy Down Fees transaction support
53108227ae is described below

commit 53108227ae5e217e5dcbc33a50e7a569c1c5608e
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Thu Jun 19 21:14:17 2025 -0500

    FINERACT-2311: Add Buy Down Fees transaction support
---
 .../loanaccount/domain/LoanTransactionType.java    |  4 +--
 .../loanaccount/domain/LoanBuyDownFeeBalance.java  |  1 +
 .../api/LoanTransactionsApiResource.java           |  3 ++-
 ...uyDownFeeAmortizationProcessingServiceImpl.java |  8 +++---
 .../service/LoanReadPlatformServiceImpl.java       | 31 +++++++++++++++++++---
 .../starter/LoanAccountConfiguration.java          |  4 +--
 .../integrationtests/LoanTransactionTest.java      | 29 ++++++++++++++++++++
 .../common/ExternalEventConfigurationHelper.java   | 18 ++++++-------
 8 files changed, 76 insertions(+), 22 deletions(-)

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 41489897c6..d1a6ede9f1 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 ed6ce482ef..679ee0ebc9 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 366d1d022d..3d0ef8d180 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 979bb4c065..c186d42812 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<Long> existingTransactionIds = 
loanTransactionRepository.findTransactionIdsByLoan(loan);
         final List<Long> existingReversedTransactionIds = 
loanTransactionRepository.findReversedTransactionIdsByLoan(loan);
 
-        List<LoanBuyDownFeeBalance> balances = 
loanBuyDownFeesBalanceRepository.findAllByLoanId(loan.getId());
+        List<LoanBuyDownFeeBalance> 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 a409e09912..95113011ca 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 a891a2aebf..60358ea5f7 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 16db6e0e90..62e515239a 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 bc4c92951b..5970b58265 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<String, Object> loanBuyDownFeeTransactionCreatedBusinessEvent = 
new HashMap<>();
-        loanBuyDownFeeTransactionCreatedBusinessEvent.put("type", 
"LoanBuyDownFeeTransactionCreatedBusinessEvent");
-        loanBuyDownFeeTransactionCreatedBusinessEvent.put("enabled", false);
-        defaults.add(loanBuyDownFeeTransactionCreatedBusinessEvent);
-
-        Map<String, Object> 
loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent = new HashMap<>();
-        loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent.put("type", 
"LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent");
-        loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent.put("enabled", 
false);
-        defaults.add(loanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent);
+        Map<String, Object> loanTransactionBuyDownFeePostBusinessEvent = new 
HashMap<>();
+        loanTransactionBuyDownFeePostBusinessEvent.put("type", 
"LoanBuyDownFeeTransactionCreatedBusinessEvent");
+        loanTransactionBuyDownFeePostBusinessEvent.put("enabled", false);
+        defaults.add(loanTransactionBuyDownFeePostBusinessEvent);
+
+        Map<String, Object> 
loanTransactionBuyDownFeeAdjustmentPostBusinessEvent = new HashMap<>();
+        loanTransactionBuyDownFeeAdjustmentPostBusinessEvent.put("type", 
"LoanBuyDownFeeAdjustmentTransactionCreatedBusinessEvent");
+        loanTransactionBuyDownFeeAdjustmentPostBusinessEvent.put("enabled", 
false);
+        defaults.add(loanTransactionBuyDownFeeAdjustmentPostBusinessEvent);
 
         Map<String, Object> 
loanBuyDownFeeAmortizationTransactionCreatedBusinessEvent = new HashMap<>();
         loanBuyDownFeeAmortizationTransactionCreatedBusinessEvent.put("type", 
"LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent");

Reply via email to