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 544607a05a FINERACT-2232: Capitalized Incomce Adjustment template
544607a05a is described below

commit 544607a05a5898182e798bf5f4cde9aef6a3d94e
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Fri May 9 12:05:37 2025 -0500

    FINERACT-2232: Capitalized Incomce Adjustment template
---
 .../apache/fineract/test/data/TransactionType.java |  4 +-
 .../test/stepdef/loan/LoanRepaymentStepDef.java    |  2 +-
 .../loanaccount/api/LoanApiConstants.java          |  1 +
 .../api/LoanTransactionApiConstants.java           |  1 +
 .../loanaccount/data/LoanTransactionData.java      | 30 ++++++++
 .../loanaccount/domain/LoanTransaction.java        |  4 ++
 .../loanaccount/domain/LoanTransactionType.java    |  6 ++
 .../service/LoanReadPlatformService.java           |  2 +-
 .../loanproduct/service/LoanEnumerations.java      |  3 +
 .../CapitalizedIncomeWritePlatformServiceImpl.java |  5 +-
 .../api/LoanTransactionsApiResource.java           | 20 ++++--
 .../service/LoanReadPlatformServiceImpl.java       | 79 +++++++++++++++++++++-
 .../starter/LoanAccountConfiguration.java          |  7 +-
 .../integrationtests/BaseLoanIntegrationTest.java  |  2 +-
 .../integrationtests/LoanTransactionTest.java      | 62 +++++++++++++++--
 .../common/loans/LoanTransactionHelper.java        | 17 ++++-
 16 files changed, 221 insertions(+), 24 deletions(-)

diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
index cd73614f3d..ebb16e6577 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
@@ -35,7 +35,9 @@ public enum TransactionType {
     INTEREST_PAYMENT_WAIVER("interestPaymentWaiver"), //
     REPAYMENT_AT_DISBURSEMENT("repaymentAtDisbursement"), //
     CAPITALIZED_INCOME("capitalizedIncome"), //
-    CAPITALIZED_INCOME_AMORTIZATION("capitalizedIncomeAmortization"); //
+    CAPITALIZED_INCOME_AMORTIZATION("capitalizedIncomeAmortization"), //
+    CAPITALIZED_INCOME_ADJUSTMENT("capitalizedIncomeAdjustment"), //
+    ;
 
     public final String value;
 
diff --git 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java
 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java
index e0e0e93705..6530aede29 100644
--- 
a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java
+++ 
b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanRepaymentStepDef.java
@@ -579,7 +579,7 @@ public class LoanRepaymentStepDef extends AbstractStepDef {
         Response<PostLoansResponse> loanResponse = 
testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId1 = loanResponse.body().getLoanId();
         Response<GetLoansLoanIdTransactionsTemplateResponse> response = 
loanTransactionsApi
-                .retrieveTransactionTemplate(loanId1, "prepayLoan", 
DATE_FORMAT, transactionDate, DEFAULT_LOCALE).execute();
+                .retrieveTransactionTemplate(loanId1, "prepayLoan", 
DATE_FORMAT, transactionDate, DEFAULT_LOCALE, null).execute();
         Double transactionAmount = response.body().getAmount();
 
         log.debug("%n--- Loan Pay-off with amount: {} ---", transactionAmount);
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index 62dfbd146e..e6db7ed52b 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -175,6 +175,7 @@ public interface LoanApiConstants {
     String CHARGEBACK_TRANSACTION_COMMAND = "chargeback";
     String MARK_AS_FRAUD_COMMAND = "markAsFraud";
     String CAPITALIZED_INCOME_TRANSACTION_COMMAND = "capitalizedIncome";
+    String CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND = 
"capitalizedIncomeAdjustment";
 
     // Data Validator names
     String LOAN_FRAUD_DATAVALIDATOR_PREFIX = "loans.fraud";
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java
index 70de654919..23a426ec77 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionApiConstants.java
@@ -54,5 +54,6 @@ public interface LoanTransactionApiConstants {
         accrualAdjustment, //
         capitalizedIncome, //
         capitalizedIncomeAmortization, //
+        capitalizedIncomeAdjustment, //
     }
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
index c9a9ced865..abcf285169 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
@@ -313,6 +313,36 @@ public class LoanTransactionData implements Serializable {
                 null, null, outstandingLoanBalance, null, manuallyReversed, 
ExternalId.empty(), null, loanId);
     }
 
+    public static LoanTransactionData 
loanTransactionDataForCreditTemplate(final LoanTransactionEnumData 
transactionType,
+            final LocalDate transactionDate, final BigDecimal 
transactionAmount, final Collection<PaymentTypeData> paymentOptions,
+            final CurrencyData currency) {
+        final Long id = null;
+        final Long loanId = null;
+        final ExternalId externalLoanId = ExternalId.empty();
+        final ExternalId externalId = ExternalId.empty();
+        final Long officeId = null;
+        final String officeName = null;
+        final PaymentDetailData paymentDetailData = null;
+        final BigDecimal unrecognizedIncomePortion = null;
+        final BigDecimal principalPortion = null;
+        final BigDecimal interestPortion = null;
+        final BigDecimal feeChargesPortion = null;
+        final BigDecimal penaltyChargesPortion = null;
+        final BigDecimal overpaymentPortion = null;
+        final BigDecimal netDisbursalAmount = null;
+        final BigDecimal fixedEmiAmount = null;
+        final BigDecimal outstandingLoanBalance = null;
+        final AccountTransferData transfer = null;
+        final LocalDate submittedOnDate = null;
+        final LocalDate possibleNextRepaymentDate = null;
+        final boolean manuallyReversed = false;
+        return new LoanTransactionData(id, officeId, officeName, 
transactionType, paymentDetailData, currency, transactionDate,
+                transactionAmount, netDisbursalAmount, principalPortion, 
interestPortion, feeChargesPortion, penaltyChargesPortion,
+                overpaymentPortion, unrecognizedIncomePortion, paymentOptions, 
transfer, externalId, fixedEmiAmount, outstandingLoanBalance,
+                submittedOnDate, manuallyReversed, possibleNextRepaymentDate, 
loanId, externalLoanId);
+
+    }
+
     public static LoanTransactionData 
loanTransactionDataForDisbursalTemplate(final LoanTransactionEnumData 
transactionType,
             final LocalDate expectedDisbursedOnLocalDateForTemplate, final 
BigDecimal disburseAmountForTemplate,
             final BigDecimal netDisbursalAmount, final 
Collection<PaymentTypeData> paymentOptions, final BigDecimal 
retriveLastEmiAmount,
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index 55a29f5e73..47f4ef5303 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -650,6 +650,10 @@ public class LoanTransaction extends 
AbstractAuditableWithUTCDateTimeCustom<Long
         return 
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.equals(getTypeOf()) && 
isNotReversed();
     }
 
+    public boolean isCapitalizedIncomeAdjustment() {
+        return 
LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.equals(getTypeOf()) && 
isNotReversed();
+    }
+
     public boolean isWaiver() {
         return isInterestWaiver() || isChargesWaiver();
     }
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 195b4e4363..2eb37e7a27 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
@@ -69,6 +69,7 @@ public enum LoanTransactionType {
     ACCRUAL_ADJUSTMENT(34, "loanTransactionType.accrualAdjustment"), //
     CAPITALIZED_INCOME(35, "loanTransactionType.capitalizedIncome"), //
     CAPITALIZED_INCOME_AMORTIZATION(36, 
"loanTransactionType.capitalizedIncomeAmortization"), //
+    CAPITALIZED_INCOME_ADJUSTMENT(37, 
"loanTransactionType.capitalizedIncomeAdjustment"), //
     ;
 
     private final Integer value;
@@ -121,6 +122,7 @@ public enum LoanTransactionType {
             case 34 -> LoanTransactionType.ACCRUAL_ADJUSTMENT;
             case 35 -> LoanTransactionType.CAPITALIZED_INCOME;
             case 36 -> LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION;
+            case 37 -> LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT;
             default -> LoanTransactionType.INVALID;
         };
     }
@@ -241,4 +243,8 @@ public enum LoanTransactionType {
     public boolean isCapitalizedIncome() {
         return this == LoanTransactionType.CAPITALIZED_INCOME;
     }
+
+    public boolean isCapitalizedIncomeAdjustment() {
+        return this == LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT;
+    }
 }
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 c81a5d73f0..cdff9421ba 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
@@ -63,7 +63,7 @@ public interface LoanReadPlatformService {
 
     LoanTransactionData retrieveLoanTransactionTemplate(Long loanId);
 
-    LoanTransactionData retrieveLoanTransactionTemplate(Long loanId, 
LoanTransactionType transactionType);
+    LoanTransactionData retrieveLoanTransactionTemplate(Long loanId, 
LoanTransactionType transactionType, Long transactionId);
 
     LoanTransactionData retrieveWaiveInterestDetails(Long loanId);
 
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
index f3c77f5725..807def9364 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
@@ -330,6 +330,9 @@ public final class LoanEnumerations {
             case CAPITALIZED_INCOME_AMORTIZATION ->
                 new 
LoanTransactionEnumData(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.getValue().longValue(),
                         
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.getCode(), "Capitalized 
Income Amortization");
+            case CAPITALIZED_INCOME_ADJUSTMENT ->
+                new 
LoanTransactionEnumData(LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getValue().longValue(),
+                        
LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getCode(), "Capitalized 
Income Adjustment");
         };
     }
 
diff --git 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
index af4d6d344f..6c22ce20ab 100644
--- 
a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
+++ 
b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java
@@ -93,7 +93,10 @@ public class CapitalizedIncomeWritePlatformServiceImpl 
implements CapitalizedInc
         // Post journal entries
         journalEntryPoster.postJournalEntries(loan, existingTransactionIds, 
existingReversedTransactionIds);
 
-        return new 
CommandProcessingResultBuilder().withEntityId(loan.getId()).withEntityExternalId(loan.getExternalId()).build();
+        return new CommandProcessingResultBuilder() //
+                .withEntityId(capitalizedIncomeTransaction.getId()) //
+                
.withEntityExternalId(capitalizedIncomeTransaction.getExternalId()) //
+                .build();
     }
 
     @Override
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 68dbc254be..89a1c58983 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,6 +91,7 @@ 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";
     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));
 
@@ -126,11 +127,12 @@ public class LoanTransactionsApiResource {
             @QueryParam("command") @Parameter(description = "command") final 
String commandParam, @Context final UriInfo uriInfo,
             @QueryParam("dateFormat") @Parameter(description = "dateFormat") 
final String rawDateFormat,
             @QueryParam("transactionDate") @Parameter(description = 
"transactionDate") final DateParam transactionDateParam,
-            @QueryParam("locale") @Parameter(description = "locale") final 
String locale) {
+            @QueryParam("locale") @Parameter(description = "locale") final 
String locale,
+            @QueryParam("transactionId") @Parameter(description = 
"transactionId") final Long transactionId) {
 
         final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? 
null : new DateFormat(rawDateFormat);
 
-        return retrieveTransactionTemplate(loanId, null, commandParam, 
uriInfo, dateFormat, transactionDateParam, locale);
+        return retrieveTransactionTemplate(loanId, null, commandParam, 
uriInfo, dateFormat, transactionDateParam, locale, transactionId);
     }
 
     @GET
@@ -156,11 +158,13 @@ public class LoanTransactionsApiResource {
             @QueryParam("command") @Parameter(description = "command") final 
String commandParam, @Context final UriInfo uriInfo,
             @QueryParam("dateFormat") @Parameter(description = "dateFormat") 
final String rawDateFormat,
             @QueryParam("transactionDate") @Parameter(description = 
"transactionDate") final DateParam transactionDateParam,
-            @QueryParam("locale") @Parameter(description = "locale") final 
String locale) {
+            @QueryParam("locale") @Parameter(description = "locale") final 
String locale,
+            @QueryParam("transactionId") @Parameter(description = 
"transactionId") final Long transactionId) {
 
         final DateFormat dateFormat = StringUtils.isBlank(rawDateFormat) ? 
null : new DateFormat(rawDateFormat);
 
-        return retrieveTransactionTemplate(null, loanExternalId, commandParam, 
uriInfo, dateFormat, transactionDateParam, locale);
+        return retrieveTransactionTemplate(null, loanExternalId, commandParam, 
uriInfo, dateFormat, transactionDateParam, locale,
+                transactionId);
     }
 
     @GET
@@ -529,6 +533,7 @@ public class LoanTransactionsApiResource {
             case accrualAdjustment -> LoanTransactionType.ACCRUAL_ADJUSTMENT;
             case capitalizedIncome -> LoanTransactionType.CAPITALIZED_INCOME;
             case capitalizedIncomeAmortization -> 
LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION;
+            case capitalizedIncomeAdjustment -> 
LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT;
             default ->
                 throw new InvalidLoanTransactionTypeException("transaction", 
transactionTypeParam.name(), "Unknown transaction type");
         };
@@ -598,7 +603,7 @@ public class LoanTransactionsApiResource {
     }
 
     private String retrieveTransactionTemplate(Long loanId, String 
loanExternalIdStr, String commandParam, UriInfo uriInfo,
-            DateFormat dateFormat, DateParam transactionDateParam, String 
locale) {
+            DateFormat dateFormat, DateParam transactionDateParam, String 
locale, Long transactionId) {
         
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
 
         ExternalId loanExternalId = 
ExternalIdFactory.produce(loanExternalIdStr);
@@ -671,7 +676,10 @@ public class LoanTransactionsApiResource {
             transactionData = 
this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId);
         } else if (CommandParameterUtil.is(commandParam, 
LoanApiConstants.CAPITALIZED_INCOME_TRANSACTION_COMMAND)) {
             transactionData = 
this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId,
-                    LoanTransactionType.CAPITALIZED_INCOME);
+                    LoanTransactionType.CAPITALIZED_INCOME, transactionId);
+        } else if (CommandParameterUtil.is(commandParam, 
LoanApiConstants.CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND)) {
+            transactionData = 
this.loanReadPlatformService.retrieveLoanTransactionTemplate(resolvedLoanId,
+                    LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT, 
transactionId);
         } else {
             throw new UnrecognizedQueryParamException("command", commandParam);
         }
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 c7b4a45eac..ca2154bda9 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
@@ -50,6 +50,7 @@ import 
org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
 import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.infrastructure.core.service.Page;
 import org.apache.fineract.infrastructure.core.service.PaginationHelper;
 import org.apache.fineract.infrastructure.core.service.SearchParameters;
@@ -107,6 +108,7 @@ import 
org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
 import 
org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
 import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeCalculationType;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeStrategy;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeType;
@@ -126,6 +128,7 @@ import 
org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanSc
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
 import 
org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
 import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionMapper;
+import 
org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanForeclosureValidator;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
 import 
org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
@@ -180,6 +183,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
     private final LoanTransactionMapper loanTransactionMapper;
     private final LoanTransactionProcessingService 
loadTransactionProcessingService;
     private final LoanBalanceService loanBalanceService;
+    private final LoanCapitalizedIncomeBalanceRepository 
loanCapitalizedIncomeBalanceRepository;
 
     @Override
     public LoanAccountData retrieveOne(final Long loanId) {
@@ -451,10 +455,52 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
         return loanDetails;
     }
 
+    private CurrencyData retriveLoanCurrencyData(final Long loanId) {
+        final LoanCurrencyDataMapper loanCurrencyMapper = new 
LoanCurrencyDataMapper(sqlGenerator);
+        final String sql = "select " + loanCurrencyMapper.schema() + " where 
l.id = ?";
+
+        return this.jdbcTemplate.queryForObject(sql, loanCurrencyMapper, 
loanId);
+    }
+
     @Override
-    public LoanTransactionData retrieveLoanTransactionTemplate(final Long 
loanId, LoanTransactionType transactionType) {
-        return 
LoanTransactionData.templateOnTop(retrieveLoanTransactionTemplate(loanId),
-                LoanEnumerations.transactionType(transactionType));
+    public LoanTransactionData retrieveLoanTransactionTemplate(final Long 
loanId, final LoanTransactionType transactionType,
+            final Long transactionId) {
+
+        LoanTransactionData loanTransactionData = null;
+        Collection<PaymentTypeData> paymentOptions = null;
+        BigDecimal transactionAmount = BigDecimal.ZERO;
+        switch (transactionType) {
+            case CAPITALIZED_INCOME:
+                final Loan loan = 
loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
+                if 
(loan.getLoanProduct().getLoanProductRelatedDetail().isEnableIncomeCapitalization())
 {
+                    final BigDecimal capitalizedIncomeBalance = 
loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loanId).stream()
+                            
.map(LoanCapitalizedIncomeBalance::getAmount).reduce(BigDecimal.ZERO, 
BigDecimal::add);
+                    transactionAmount = 
loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()).subtract(capitalizedIncomeBalance);
+                }
+                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
+                        .findByLoanIdAndLoanTransactionId(loanId, 
transactionId);
+
+                transactionAmount = (loanCapitalizedIncomeBalance == null) ? 
BigDecimal.ZERO
+                        : loanCapitalizedIncomeBalance.getAmount()
+                                
.subtract(MathUtil.nullToZero(loanCapitalizedIncomeBalance.getAmountAdjustment()));
+                loanTransactionData = 
LoanTransactionData.loanTransactionDataForCreditTemplate(
+                        LoanEnumerations.transactionType(transactionType), 
DateUtils.getBusinessLocalDate(), transactionAmount,
+                        paymentOptions, retriveLoanCurrencyData(loanId));
+            break;
+            default:
+                loanTransactionData = 
LoanTransactionData.templateOnTop(retrieveLoanTransactionTemplate(loanId),
+                        LoanEnumerations.transactionType(transactionType));
+            break;
+        }
+
+        return loanTransactionData;
     }
 
     @Override
@@ -646,6 +692,33 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
         return loanId;
     }
 
+    private static final class LoanCurrencyDataMapper implements 
RowMapper<CurrencyData> {
+
+        private final DatabaseSpecificSQLGenerator sqlGenerator;
+
+        LoanCurrencyDataMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
+            this.sqlGenerator = sqlGenerator;
+        }
+
+        public String schema() {
+            return " l.currency_code as currencyCode, l.currency_digits as 
currencyDigits, l.currency_multiplesof as inMultiplesOf, rc."
+                    + sqlGenerator.escape("name")
+                    + " as currencyName, rc.display_symbol as 
currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode from 
m_loan l join m_currency rc on rc."
+                    + sqlGenerator.escape("code") + " = l.currency_code ";
+        }
+
+        @Override
+        public CurrencyData mapRow(final ResultSet rs, 
@SuppressWarnings("unused") final int rowNum) throws SQLException {
+            final String currencyCode = rs.getString("currencyCode");
+            final String currencyName = rs.getString("currencyName");
+            final String currencyNameCode = rs.getString("currencyNameCode");
+            final String currencyDisplaySymbol = 
rs.getString("currencyDisplaySymbol");
+            final Integer currencyDigits = JdbcSupport.getInteger(rs, 
"currencyDigits");
+            final Integer inMultiplesOf = JdbcSupport.getInteger(rs, 
"inMultiplesOf");
+            return new CurrencyData(currencyCode, currencyName, 
currencyDigits, inMultiplesOf, currencyDisplaySymbol, currencyNameCode);
+        }
+    }
+
     private static final class LoanMapper implements 
RowMapper<LoanAccountData> {
 
         private final DatabaseSpecificSQLGenerator sqlGenerator;
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 47f02d53ea..3dfa3bcf0d 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
@@ -88,6 +88,7 @@ import 
org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper;
 import 
org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper;
 import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper;
 import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionMapper;
+import 
org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
 import 
org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanTermVariationsRepository;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationTransitionValidator;
 import 
org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator;
@@ -333,14 +334,16 @@ public class LoanAccountConfiguration {
             DelinquencyReadPlatformService delinquencyReadPlatformService, 
LoanTransactionRepository loanTransactionRepository,
             LoanChargePaidByReadService loanChargePaidByReadService, 
LoanTransactionRelationReadService loanTransactionRelationReadService,
             LoanForeclosureValidator loanForeclosureValidator, 
LoanTransactionMapper loanTransactionMapper,
-            LoanTransactionProcessingService loanTransactionProcessingService, 
LoanBalanceService loanBalanceService) {
+            LoanTransactionProcessingService loanTransactionProcessingService, 
LoanBalanceService loanBalanceService,
+            LoanCapitalizedIncomeBalanceRepository 
loanCapitalizedIncomeBalanceRepository) {
         return new LoanReadPlatformServiceImpl(jdbcTemplate, context, 
loanRepositoryWrapper, applicationCurrencyRepository,
                 loanProductReadPlatformService, clientReadPlatformService, 
groupReadPlatformService, loanDropdownReadPlatformService,
                 fundReadPlatformService, chargeReadPlatformService, 
codeValueReadPlatformService, calendarReadPlatformService,
                 staffReadPlatformService, paginationHelper, 
paymentTypeReadPlatformService, floatingRatesReadPlatformService,
                 loanUtilService, configurationDomainService, 
accountDetailsReadPlatformService, columnValidator, sqlGenerator,
                 delinquencyReadPlatformService, loanTransactionRepository, 
loanChargePaidByReadService, loanTransactionRelationReadService,
-                loanForeclosureValidator, loanTransactionMapper, 
loanTransactionProcessingService, loanBalanceService);
+                loanForeclosureValidator, loanTransactionMapper, 
loanTransactionProcessingService, loanBalanceService,
+                loanCapitalizedIncomeBalanceRepository);
     }
 
     @Bean
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index 40030968b4..2721be9376 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -276,7 +276,7 @@ public abstract class BaseLoanIntegrationTest extends 
IntegrationTest {
     }
 
     protected GetLoansLoanIdTransactionsTemplateResponse getPrepayAmount(Long 
loanId, String date) {
-        return 
ok(fineractClient().loanTransactions.retrieveTransactionTemplate(loanId, 
"prepayLoan", DATETIME_PATTERN, date, "en"));
+        return 
ok(fineractClient().loanTransactions.retrieveTransactionTemplate(loanId, 
"prepayLoan", DATETIME_PATTERN, date, "en", null));
     }
 
     protected Long verifyPrepayAmountByRepayment(Long loanId, String date) {
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 63b0c6cb64..e159f2b849 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
@@ -25,17 +25,25 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.client.models.GetLoansLoanIdTransactionsResponse;
 import 
org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse;
 import org.apache.fineract.client.models.PostClientsResponse;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
 import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import org.apache.fineract.client.models.TransactionType;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
+@Slf4j
 public class LoanTransactionTest extends BaseLoanIntegrationTest {
 
+    private final String capitalizedIncomeCommand = "capitalizedIncome";
+    private final String capitalizedIncomeAdjustmentCommand = 
"capitalizedIncomeAdjustment";
+
     @Test
     public void testGetLoanTransactionsFiltering() {
         final PostClientsResponse client = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
@@ -94,24 +102,68 @@ public class LoanTransactionTest extends 
BaseLoanIntegrationTest {
     public void testGetLoanTransactionTemplateForCapitalizedIncome() {
         final PostClientsResponse client = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
 
-        final PostLoanProductsResponse loanProductsResponse = 
loanProductHelper.createLoanProduct(create4IProgressive());
+        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();
 
-        final String command = "capitalizedIncome";
         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(430), "20 December 2024");
+            disburseLoan(loanId, BigDecimal.valueOf(230), "20 December 2024");
 
             final GetLoansLoanIdTransactionsTemplateResponse 
transactionTemplate = loanTransactionHelper.retrieveTransactionTemplate(loanId,
-                    command, null, null, null);
+                    capitalizedIncomeCommand, null, null, null);
 
             assertNotNull(transactionTemplate);
-            assertEquals("loanTransactionType." + command, 
transactionTemplate.getType().getCode());
+            assertEquals("loanTransactionType." + capitalizedIncomeCommand, 
transactionTemplate.getType().getCode());
+            assertEquals(transactionTemplate.getAmount(), 200);
             assertThat(transactionTemplate.getPaymentTypeOptions().size() > 0);
         });
     }
 
+    @Test
+    public void testGetLoanTransactionTemplateForCapitalizedIncomeAdjustment() 
{
+        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", () -> {
+            final 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");
+
+            PostLoansLoanIdTransactionsResponse loanTransactionResponse = 
loanTransactionHelper.executeLoanTransaction(loanId,
+                    new 
PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN).transactionDate("20
 December 2024").locale("en")
+                            .transactionAmount(150.0),
+                    capitalizedIncomeCommand);
+            assertNotNull(loanTransactionResponse);
+            final Long transactionId = loanTransactionResponse.getResourceId();
+            assertNotNull(transactionId);
+            log.info("Loan Id {} with transaction id {}", loanId, 
transactionId);
+
+            final GetLoansLoanIdTransactionsTemplateResponse 
transactionTemplate = loanTransactionHelper.retrieveTransactionTemplate(loanId,
+                    capitalizedIncomeAdjustmentCommand, null, null, null, 
transactionId);
+
+            assertNotNull(transactionTemplate);
+            assertEquals("loanTransactionType." + 
capitalizedIncomeAdjustmentCommand, transactionTemplate.getType().getCode());
+            assertEquals(transactionTemplate.getAmount(), 150);
+        });
+    }
+
 }
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 8918a5a146..5de3e0244b 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
@@ -902,6 +902,11 @@ public class LoanTransactionHelper {
         return 
postLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID), 
getRepaymentBodyAsJSON(date, amountToBePaid));
     }
 
+    public PostLoansLoanIdTransactionsResponse executeLoanTransaction(final 
Long loanId, final PostLoansLoanIdTransactionsRequest request,
+            final String command) {
+        return 
Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.executeLoanTransaction(loanId,
 request, command));
+    }
+
     public PostLoansLoanIdTransactionsResponse makeLoanRepayment(final Long 
loanId, final PostLoansLoanIdTransactionsRequest request) {
         return 
Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.executeLoanTransaction(loanId,
 request, "repayment"));
     }
@@ -2332,7 +2337,7 @@ public class LoanTransactionHelper {
     public GetLoansLoanIdTransactionsTemplateResponse 
getPrepaymentAmount(final Long loanId, final String transactionDate,
             String dateformat) {
         return 
Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate(loanId,
 "prepayLoan",
-                dateformat, transactionDate, "en"));
+                dateformat, transactionDate, "en", null));
     }
 
     // TODO: Rewrite to use fineract-client instead!
@@ -2806,16 +2811,22 @@ public class LoanTransactionHelper {
         return chargebackPayload;
     }
 
+    public GetLoansLoanIdTransactionsTemplateResponse 
retrieveTransactionTemplate(Long loanId, String command, String dateFormat,
+            String transactionDate, String locale, Long transactionId) {
+        return 
Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate(loanId,
 command, dateFormat,
+                transactionDate, locale, transactionId));
+    }
+
     public GetLoansLoanIdTransactionsTemplateResponse 
retrieveTransactionTemplate(Long loanId, String command, String dateFormat,
             String transactionDate, String locale) {
         return 
Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate(loanId,
 command, dateFormat,
-                transactionDate, locale));
+                transactionDate, locale, null));
     }
 
     public GetLoansLoanIdTransactionsTemplateResponse 
retrieveTransactionTemplate(String loanExternalIdStr, String command,
             String dateFormat, String transactionDate, String locale) {
         return 
Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.retrieveTransactionTemplate1(loanExternalIdStr,
 command,
-                dateFormat, transactionDate, locale));
+                dateFormat, transactionDate, locale, null));
     }
 
     public GetLoansApprovalTemplateResponse getLoanApprovalTemplate(String 
loanExternalIdStr) {


Reply via email to