You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2025/05/29 14:19:35 UTC

(fineract) 01/03: FINERACT-2232: Capitalized income adjustment

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 82eafad8dd344706bf1b5ee68235f8c6c899818e
Author: adam.magyari <ma...@gmail.com>
AuthorDate: Tue May 20 11:08:20 2025 +0200

    FINERACT-2232: Capitalized income adjustment
---
 .../commands/service/CommandWrapperBuilder.java    |   9 +
 ...AdjustmentTransactionCreatedBusinessEvent.java} |  20 ++-
 .../loanaccount/data/LoanTransactionEnumData.java  |   2 +
 .../loanaccount/domain/LoanTransaction.java        |   9 +-
 .../domain/LoanTransactionRelationTypeEnum.java    |   4 +-
 .../service/CapitalizedIncomePlatformService.java  |   3 +
 .../domain/PaymentAllocationTransactionType.java   |   3 +-
 ...dvancedPaymentScheduleTransactionProcessor.java |   2 +-
 .../CapitalizedIncomeAdjustmentCommandHandler.java |  53 ++++++
 .../LoanCapitalizedIncomeBalanceRepository.java    |   3 +
 .../CapitalizedIncomeWritePlatformServiceImpl.java |  57 +++++-
 .../ProgressiveLoanTransactionValidator.java       |   2 +
 .../ProgressiveLoanTransactionValidatorImpl.java   |  81 +++++++++
 .../ProgressiveLoanAccountConfiguration.java       |   5 +-
 .../AccrualBasedAccountingProcessorForLoan.java    | 131 ++++++++++++++
 .../api/LoanTransactionsApiResource.java           |   2 +
 .../loanaccount/api/LoansApiResourceSwagger.java   |   2 +
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 ...d_capitalized_income_adjustment_transaction.xml |  38 ++++
 ...nalEventConfigurationValidationServiceTest.java |   6 +-
 .../LoanCapitalizedIncomeTest.java                 | 198 +++++++++++++++++++++
 .../common/ExternalEventConfigurationHelper.java   |   6 +
 .../common/loans/LoanTransactionHelper.java        |  30 ++++
 23 files changed, 649 insertions(+), 18 deletions(-)

diff --git a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index a3389825db..872835ffd7 100644
--- a/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ b/fineract-core/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -3831,4 +3831,13 @@ public class CommandWrapperBuilder {
         this.href = "/loans/" + loanId;
         return this;
     }
+
+    public CommandWrapperBuilder capitalizedIncomeAdjustment(final Long loanId, final Long transactionId) {
+        this.actionName = "CAPITALIZEDINCOMEADJUSTMENT";
+        this.entityName = "LOAN";
+        this.entityId = transactionId;
+        this.loanId = loanId;
+        this.href = "/loans/" + loanId + "/transactions/" + transactionId;
+        return this;
+    }
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent.java
similarity index 58%
copy from fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java
copy to fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent.java
index 0c1879323b..ad9ffd961b 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/transaction/LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent.java
@@ -16,16 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.loanaccount.service;
+package org.apache.fineract.infrastructure.event.business.domain.loan.transaction;
 
-import org.apache.fineract.infrastructure.core.api.JsonCommand;
-import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
-import org.springframework.transaction.annotation.Transactional;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 
-public interface CapitalizedIncomePlatformService {
+public class LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent extends LoanTransactionBusinessEvent {
 
-    @Transactional
-    CommandProcessingResult addCapitalizedIncome(Long loanId, JsonCommand command);
+    private static final String TYPE = "LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent";
 
-    void resetBalance(Long loanId);
+    public LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent(LoanTransaction value) {
+        super(value);
+    }
+
+    @Override
+    public String getType() {
+        return TYPE;
+    }
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java
index 573b45703b..5b520eee54 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java
@@ -69,6 +69,7 @@ public class LoanTransactionEnumData implements Serializable {
     private final boolean accrualAdjustment;
     private final boolean capitalizedIncome;
     private final boolean capitalizedIncomeAmortization;
+    private final boolean capitalizedIncomeAdjustment;
 
     public LoanTransactionEnumData(final Long id, final String code, final String value) {
         this.id = id;
@@ -107,6 +108,7 @@ public class LoanTransactionEnumData implements Serializable {
         this.accrualAdjustment = Long.valueOf(LoanTransactionType.ACCRUAL_ADJUSTMENT.getValue()).equals(this.id);
         this.capitalizedIncome = Long.valueOf(LoanTransactionType.CAPITALIZED_INCOME.getValue()).equals(this.id);
         this.capitalizedIncomeAmortization = Long.valueOf(LoanTransactionType.CAPITALIZED_INCOME_AMORTIZATION.getValue()).equals(this.id);
+        this.capitalizedIncomeAdjustment = Long.valueOf(LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT.getValue()).equals(this.id);
     }
 
     public boolean isRepaymentType() {
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 47f4ef5303..80adc30e51 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
@@ -322,6 +322,12 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
         };
     }
 
+    public static LoanTransaction capitalizedIncomeAdjustment(final Loan loan, final Money amount, final PaymentDetail paymentDetail,
+            final LocalDate transactionDate, final ExternalId externalId) {
+        return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT, transactionDate,
+                amount.getAmount(), amount.getAmount(), null, null, null, null, false, paymentDetail, externalId);
+    }
+
     public LoanTransaction copyTransactionPropertiesAndMappings() {
         LoanTransaction newTransaction = copyTransactionProperties(this);
         newTransaction.updateLoanTransactionToRepaymentScheduleMappings(loanTransactionToRepaymentScheduleMappings);
@@ -570,7 +576,8 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long
 
     public boolean isRepaymentLikeType() {
         return isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit() || isChargeRefund()
-                || isChargeAdjustment() || isDownPayment() || isInterestPaymentWaiver() || isInterestRefund();
+                || isChargeAdjustment() || isDownPayment() || isInterestPaymentWaiver() || isInterestRefund()
+                || isCapitalizedIncomeAdjustment();
     }
 
     public boolean isTypeAllowedForChargeback() {
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java
index 52cbec85e0..85eae4b28c 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java
@@ -24,7 +24,9 @@ public enum LoanTransactionRelationTypeEnum {
     CHARGEBACK(1, "loanTransactionRelationType.chargeback"), //
     CHARGE_ADJUSTMENT(2, "loanTransactionRelationType.chargeAdjustment"), //
     REPLAYED(3, "loanTransactionRelationType.replayed"), //
-    RELATED(4, "loanTransactionRelationType.related");
+    RELATED(4, "loanTransactionRelationType.related"), //
+    ADJUSTMENT(5, "loanTransactionRelationType.adjustment"), //
+    ;
 
     private final Integer value;
     private final String code;
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java
index 0c1879323b..b0bacc6d84 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomePlatformService.java
@@ -27,5 +27,8 @@ public interface CapitalizedIncomePlatformService {
     @Transactional
     CommandProcessingResult addCapitalizedIncome(Long loanId, JsonCommand command);
 
+    @Transactional
+    CommandProcessingResult capitalizedIncomeAdjustment(Long loanId, Long capitalizedIncomeTransactionId, JsonCommand command);
+
     void resetBalance(Long loanId);
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
index 942f3de612..6194a99de2 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/domain/PaymentAllocationTransactionType.java
@@ -42,7 +42,8 @@ public enum PaymentAllocationTransactionType {
     CHARGE_PAYMENT(LoanTransactionType.CHARGE_PAYMENT, "Charge payment"), //
     REFUND_FOR_ACTIVE_LOAN(LoanTransactionType.REFUND_FOR_ACTIVE_LOAN, "Refund for active loan"), //
     INTEREST_PAYMENT_WAIVER(LoanTransactionType.INTEREST_PAYMENT_WAIVER, "Interest payment waiver"), //
-    INTEREST_REFUND(LoanTransactionType.INTEREST_REFUND, "Interest refund");
+    INTEREST_REFUND(LoanTransactionType.INTEREST_REFUND, "Interest refund"), //
+    CAPITALIZED_INCOME_ADJUSTMENT(LoanTransactionType.CAPITALIZED_INCOME_ADJUSTMENT, "Capitalized income adjustment");
 
     private final LoanTransactionType loanTransactionType;
     private final String humanReadableName;
diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index cfbbe223a5..b728e5f977 100644
--- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -335,7 +335,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
             case CHARGEBACK -> handleChargeback(loanTransaction, ctx);
             case CREDIT_BALANCE_REFUND -> handleCreditBalanceRefund(loanTransaction, ctx);
             case REPAYMENT, MERCHANT_ISSUED_REFUND, PAYOUT_REFUND, GOODWILL_CREDIT, CHARGE_REFUND, CHARGE_ADJUSTMENT, DOWN_PAYMENT,
-                    WAIVE_INTEREST, RECOVERY_REPAYMENT, INTEREST_PAYMENT_WAIVER ->
+                    WAIVE_INTEREST, RECOVERY_REPAYMENT, INTEREST_PAYMENT_WAIVER, CAPITALIZED_INCOME_ADJUSTMENT ->
                 handleRepayment(loanTransaction, ctx);
             case INTEREST_REFUND -> handleInterestRefund(loanTransaction, ctx);
             case CHARGE_OFF -> handleChargeOff(loanTransaction, ctx);
diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CapitalizedIncomeAdjustmentCommandHandler.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CapitalizedIncomeAdjustmentCommandHandler.java
new file mode 100644
index 0000000000..1815a98812
--- /dev/null
+++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CapitalizedIncomeAdjustmentCommandHandler.java
@@ -0,0 +1,53 @@
+/**
+ * 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.handler;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.DataIntegrityErrorHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.loanaccount.service.CapitalizedIncomePlatformService;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.orm.jpa.JpaSystemException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@CommandType(entity = "LOAN", action = "CAPITALIZEDINCOMEADJUSTMENT")
+public class CapitalizedIncomeAdjustmentCommandHandler implements NewCommandSourceHandler {
+
+    private final CapitalizedIncomePlatformService capitalizedIncomePlatformService;
+    private final DataIntegrityErrorHandler dataIntegrityErrorHandler;
+
+    @Transactional
+    @Override
+    public CommandProcessingResult processCommand(final JsonCommand command) {
+
+        try {
+            return this.capitalizedIncomePlatformService.capitalizedIncomeAdjustment(command.getLoanId(), command.entityId(), command);
+        } catch (final JpaSystemException | DataIntegrityViolationException dve) {
+            dataIntegrityErrorHandler.handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve, "loan.capitalized.income",
+                    "Capitalized Income");
+            return CommandProcessingResult.empty();
+        }
+    }
+}
diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanCapitalizedIncomeBalanceRepository.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanCapitalizedIncomeBalanceRepository.java
index ab5f0f1ac5..0ba8a15fc9 100644
--- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanCapitalizedIncomeBalanceRepository.java
+++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/repository/LoanCapitalizedIncomeBalanceRepository.java
@@ -43,4 +43,7 @@ public interface LoanCapitalizedIncomeBalanceRepository
 
     @Query("SELECT SUM(lcib.amountAdjustment) FROM LoanCapitalizedIncomeBalance lcib WHERE lcib.loan.id = :loanId")
     BigDecimal calculateCapitalizedIncomeAdjustment(Long loanId);
+
+    @Query("SELECT lcib FROM LoanCapitalizedIncomeBalance lcib, LoanTransaction lt, LoanTransactionRelation ltr WHERE lt.loan.id = lcib.loan.id AND ltr.fromTransaction.id =:transactionId AND ltr.toTransaction.id=lt.id AND lcib.loanTransaction.id = lt.id")
+    LoanCapitalizedIncomeBalance findBalanceForAdjustment(Long transactionId);
 }
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 d0e745756e..a931048cd3 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
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
@@ -32,10 +33,13 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 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.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
 import org.apache.fineract.portfolio.note.service.NoteWritePlatformService;
@@ -103,11 +107,60 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc
                 .build();
     }
 
-    private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction capitalizedIncomeTransaction) {
+    @Override
+    public CommandProcessingResult capitalizedIncomeAdjustment(final Long loanId, final Long capitalizedIncomeTransactionId,
+            final JsonCommand command) {
+        loanTransactionValidator.validateCapitalizedIncomeAdjustment(command, loanId, capitalizedIncomeTransactionId);
+        final Loan loan = loanAssembler.assembleFrom(loanId);
+        final List<Long> existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan));
+        final List<Long> existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan));
+        final Map<String, Object> changes = new LinkedHashMap<>();
+        // Create payment details
+        final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
+        // Extract transaction details
+        final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
+        final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
+        final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, "externalId");
+
+        Optional<LoanTransaction> capitalizedIncome = loanTransactionRepository.findById(capitalizedIncomeTransactionId);
+        LoanTransaction capitalizedIncomeAdjustment = LoanTransaction.capitalizedIncomeAdjustment(loan,
+                Money.of(loan.getCurrency(), transactionAmount), paymentDetail, transactionDate, txnExternalId);
+        capitalizedIncomeAdjustment.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(capitalizedIncomeAdjustment,
+                capitalizedIncome.get(), LoanTransactionRelationTypeEnum.ADJUSTMENT));
+        loan.addLoanTransaction(capitalizedIncomeAdjustment);
+        recalculateLoanTransactions(loan, transactionDate, capitalizedIncomeAdjustment);
+        LoanTransaction savedCapitalizedIncomeAdjustment = loanTransactionRepository.saveAndFlush(capitalizedIncomeAdjustment);
+
+        // Update outstanding loan balances
+        loan.updateLoanOutstandingBalances();
+
+        // Create a note if provided
+        final String noteText = command.stringValueOfParameterNamed("note");
+        if (noteText != null && !noteText.isEmpty()) {
+            noteWritePlatformService.createLoanTransactionNote(savedCapitalizedIncomeAdjustment.getId(), noteText);
+        }
+        // Post journal entries
+        journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+
+        // Accounting uses the original balance, balance update HAS TO HAPPEN AFTER postJournalEntries
+        LoanCapitalizedIncomeBalance capitalizedIncomeBalance = capitalizedIncomeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId,
+                capitalizedIncomeTransactionId);
+        capitalizedIncomeBalance
+                .setAmountAdjustment(MathUtil.nullToZero(capitalizedIncomeBalance.getAmountAdjustment()).add(transactionAmount));
+        capitalizedIncomeBalance.setUnrecognizedAmount(
+                MathUtil.negativeToZero(capitalizedIncomeBalance.getUnrecognizedAmount().subtract(transactionAmount)));
+        capitalizedIncomeBalanceRepository.save(capitalizedIncomeBalance);
+
+        return new CommandProcessingResultBuilder().withLoanId(loan.getId()).withLoanExternalId(loan.getExternalId())
+                .withEntityId(savedCapitalizedIncomeAdjustment.getId())
+                .withEntityExternalId(savedCapitalizedIncomeAdjustment.getExternalId()).build();
+    }
+
+    private void recalculateLoanTransactions(Loan loan, LocalDate transactionDate, LoanTransaction transaction) {
         if (loan.isInterestBearingAndInterestRecalculationEnabled() || DateUtils.isBeforeBusinessDate(transactionDate)) {
             reprocessLoanTransactionsService.reprocessTransactions(loan);
         } else {
-            reprocessLoanTransactionsService.processLatestTransaction(capitalizedIncomeTransaction, loan);
+            reprocessLoanTransactionsService.processLatestTransaction(transaction, loan);
         }
     }
 
diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidator.java
index 192039acd6..0f0d2faa02 100644
--- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidator.java
+++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidator.java
@@ -24,4 +24,6 @@ import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionVa
 public interface ProgressiveLoanTransactionValidator extends LoanTransactionValidator {
 
     void validateCapitalizedIncome(JsonCommand command, Long loanId);
+
+    void validateCapitalizedIncomeAdjustment(JsonCommand command, Long loanId, Long capitalizedIncomeTransactionId);
 }
diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java
index b2f306d414..dc3d42e6c6 100644
--- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java
+++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java
@@ -27,6 +27,7 @@ import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -35,6 +36,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
 import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.holiday.domain.Holiday;
 import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
 import org.apache.fineract.portfolio.common.service.Validator;
@@ -45,6 +47,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
 import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator;
@@ -57,6 +60,7 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
     private final LoanTransactionValidator loanTransactionValidator;
     private final LoanRepositoryWrapper loanRepositoryWrapper;
     private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     @Override
     public void validateCapitalizedIncome(final JsonCommand command, final Long loanId) {
@@ -127,6 +131,78 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
         });
     }
 
+    @Override
+    public void validateCapitalizedIncomeAdjustment(JsonCommand command, Long loanId, Long capitalizedIncomeTransactionId) {
+        final String json = command.json();
+        if (StringUtils.isBlank(json)) {
+            throw new InvalidJsonException();
+        }
+
+        final JsonElement element = this.fromApiJsonHelper.parse(json);
+        final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+        this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, getCapitalizedIncomeAdjustmentParameters());
+
+        Validator.validateOrThrow("loan.capitalized.incomeAdjustment", baseDataValidator -> {
+            final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
+            validateLoanClientIsActive(loan);
+            validateLoanGroupIsActive(loan);
+
+            // Validate loan is progressive
+            if (!loan.isProgressiveSchedule()) {
+                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.progressive.loan");
+            }
+
+            // Validate income capitalization is enabled
+            if (!loan.getLoanProductRelatedDetail().isEnableIncomeCapitalization()) {
+                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("income.capitalization.not.enabled");
+            }
+
+            // Validate loan is active
+            if (!loan.getStatus().isActive()) {
+                baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.active");
+            }
+
+            final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element);
+            baseDataValidator.reset().parameter("transactionDate").value(transactionDate).notNull();
+
+            // Validate transaction date is not before disbursement date
+            if (transactionDate != null && loan.getDisbursementDate() != null && transactionDate.isBefore(loan.getDisbursementDate())) {
+                baseDataValidator.reset().parameter("transactionDate").failWithCode("before.disbursement.date",
+                        "Transaction date cannot be before disbursement date");
+            }
+
+            final BigDecimal transactionAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("transactionAmount", element);
+            baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).notNull().positiveAmount();
+
+            Optional<LoanTransaction> capitalizedIncomeTransactionOpt = loanTransactionRepository.findById(capitalizedIncomeTransactionId);
+            if (capitalizedIncomeTransactionOpt.isEmpty()) {
+                baseDataValidator.reset().parameter("capitalizedIncomeTransactionId").failWithCode("loan.transaction.not.found",
+                        "Capitalized Income transaction not found.");
+            } else {
+                // Validate not before capitalized income transaction
+                if (transactionDate != null && transactionDate.isBefore(capitalizedIncomeTransactionOpt.get().getTransactionDate())) {
+                    baseDataValidator.reset().parameter("transactionDate").failWithCode("before.capitalizedIncome.transaction.date",
+                            "Transaction date cannot be before capitalized income transaction date");
+
+                }
+                if (transactionAmount != null) {
+                    LoanCapitalizedIncomeBalance capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
+                            .findByLoanIdAndLoanTransactionId(loanId, capitalizedIncomeTransactionId);
+                    if (MathUtil.isLessThan(capitalizedIncomeBalance.getAmount()
+                            .subtract(MathUtil.nullToZero(capitalizedIncomeBalance.getAmountAdjustment())), transactionAmount)) {
+                        baseDataValidator.reset().parameter("transactionAmount").value(transactionAmount).failWithCode(
+                                "cannot.be.more.than.remaining.amount",
+                                " Capitalized income adjustment amount cannot be more than remaining amount");
+                    }
+                }
+            }
+
+            validatePaymentDetails(baseDataValidator, element);
+            validateNote(baseDataValidator, element);
+            validateExternalId(baseDataValidator, element);
+        });
+    }
+
     // Delegates
     @Override
     public void validateDisbursement(JsonCommand command, boolean isAccountTransfer, Long loanId) {
@@ -280,4 +356,9 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT
         return new HashSet<>(
                 Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
     }
+
+    private Set<String> getCapitalizedIncomeAdjustmentParameters() {
+        return new HashSet<>(
+                Arrays.asList("transactionDate", "dateFormat", "locale", "transactionAmount", "paymentTypeId", "note", "externalId"));
+    }
 }
diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/ProgressiveLoanAccountConfiguration.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/ProgressiveLoanAccountConfiguration.java
index 31e27793b9..6694b9eab0 100644
--- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/ProgressiveLoanAccountConfiguration.java
+++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/ProgressiveLoanAccountConfiguration.java
@@ -60,9 +60,10 @@ public class ProgressiveLoanAccountConfiguration {
     @ConditionalOnMissingBean(ProgressiveLoanTransactionValidator.class)
     public ProgressiveLoanTransactionValidator progressiveLoanTransactionValidator(FromJsonHelper fromApiJsonHelper,
             LoanTransactionValidator loanTransactionValidator, LoanRepositoryWrapper loanRepositoryWrapper,
-            LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository) {
+            LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository,
+            LoanTransactionRepository loanTransactionRepository) {
         return new ProgressiveLoanTransactionValidatorImpl(fromApiJsonHelper, loanTransactionValidator, loanRepositoryWrapper,
-                loanCapitalizedIncomeBalanceRepository);
+                loanCapitalizedIncomeBalanceRepository, loanTransactionRepository);
     }
 
     @Bean
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
index 29830b566b..4aa44b87b1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
@@ -21,10 +21,12 @@ package org.apache.fineract.accounting.journalentry.service;
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import lombok.RequiredArgsConstructor;
 import org.apache.fineract.accounting.closure.domain.GLClosure;
 import org.apache.fineract.accounting.common.AccountingConstants.AccrualAccountsForLoan;
@@ -34,11 +36,21 @@ import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
 import org.apache.fineract.accounting.journalentry.data.GLAccountBalanceHolder;
 import org.apache.fineract.accounting.journalentry.data.LoanDTO;
 import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
+import org.apache.fineract.accounting.journalentry.domain.JournalEntry;
+import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
 import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
+import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.office.domain.Office;
 import org.apache.fineract.portfolio.PortfolioProductType;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBalance;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
+import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncomeBalanceRepository;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -47,6 +59,9 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
 
     private final AccountingProcessorHelper helper;
     private final JournalEntryWritePlatformService journalEntryWritePlatformService;
+    private final LoanCapitalizedIncomeBalanceRepository loanCapitalizedIncomeBalanceRepository;
+    private final JournalEntryRepository journalEntryRepository;
+    private final LoanTransactionRepository loanTransactionRepository;
 
     @Override
     public void createJournalEntriesForLoan(final LoanDTO loanDTO) {
@@ -131,6 +146,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
             if (transactionType.isCapitalizedIncomeAmortization()) {
                 createJournalEntriesForCapitalizedIncomeAmortization(loanDTO, loanTransactionDTO, office);
             }
+            // Handle Capitalized Income Adjustment
+            if (transactionType.isCapitalizedIncomeAdjustment()) {
+                createJournalEntriesForCapitalizedIncomeAdjustment(loanDTO, loanTransactionDTO, office);
+            }
         }
     }
 
@@ -171,6 +190,118 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
         }
     }
 
+    private void createJournalEntriesForCapitalizedIncomeAdjustment(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
+            final Office office) {
+        // loan properties
+        final Long loanProductId = loanDTO.getLoanProductId();
+        final Long loanId = loanDTO.getLoanId();
+        final String currencyCode = loanDTO.getCurrencyCode();
+        // transaction properties
+        final String transactionId = loanTransactionDTO.getTransactionId();
+        final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
+        final BigDecimal transactionAmount = loanTransactionDTO.getAmount();
+        final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
+        final BigDecimal interestAmount = loanTransactionDTO.getInterest();
+        final BigDecimal feesAmount = loanTransactionDTO.getFees();
+        final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
+        final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
+        final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
+        final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
+
+        if (MathUtil.isGreaterThanZero(transactionAmount)) {
+            // Resolve Credit
+            // handle principal payment
+            if (MathUtil.isGreaterThanZero(principalAmount)) {
+                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                        AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
+                glAccountBalanceHolder.addToCredit(account, principalAmount);
+            }
+            // handle interest payment
+            if (MathUtil.isGreaterThanZero(interestAmount)) {
+                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                        AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId);
+                glAccountBalanceHolder.addToCredit(account, interestAmount);
+            }
+            // handle fee payment
+            if (MathUtil.isGreaterThanZero(feesAmount)) {
+                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                        AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId);
+                glAccountBalanceHolder.addToCredit(account, feesAmount);
+            }
+            // handle penalty payment
+            if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
+                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                        AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), paymentTypeId);
+                glAccountBalanceHolder.addToCredit(account, penaltiesAmount);
+            }
+            // handle overpayment
+            if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
+                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                        AccrualAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId);
+                glAccountBalanceHolder.addToCredit(account, overPaymentAmount);
+            }
+
+            // Resolve Debit
+            GLAccount accountIncomeFromCapitalization = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.INCOME_FROM_CAPITALIZATION.getValue(), paymentTypeId);
+            GLAccount accountDeferredIncome = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.DEFERRED_INCOME_LIABILITY.getValue(), paymentTypeId);
+
+            Optional<LoanTransaction> transactionOpt = loanTransactionRepository.findById(Long.parseLong(transactionId));
+            if (transactionOpt.isEmpty()) {
+                throw new PlatformDataIntegrityException("transaction.for.accounting.not.found", "Transaction for accounting not found");
+            }
+            Optional<LoanTransactionRelation> relationToOriginalTransactionOpt = transactionOpt.get().getLoanTransactionRelations().stream()//
+                    .filter(rel -> LoanTransactionRelationTypeEnum.REPLAYED.equals(rel.getRelationType()))//
+                    .min(Comparator.comparing(AbstractPersistableCustom::getId));
+            if (relationToOriginalTransactionOpt.isPresent()) {
+                // Reverse-replay case
+                final LoanTransaction originalTransaction = relationToOriginalTransactionOpt.get().getToTransaction();
+                final List<JournalEntry> originalJournalEntries = this.journalEntryRepository.findJournalEntries(
+                        AccountingProcessorHelper.LOAN_TRANSACTION_IDENTIFIER + originalTransaction.getId(),
+                        PortfolioProductType.LOAN.getValue());
+
+                for (JournalEntry originalJournalEntry : originalJournalEntries) {
+                    if (originalJournalEntry.isDebitEntry()) {
+                        if (accountIncomeFromCapitalization.getId().equals(originalJournalEntry.getGlAccount().getId())) {
+                            glAccountBalanceHolder.addToDebit(accountIncomeFromCapitalization, originalJournalEntry.getAmount());
+                        } else if (accountDeferredIncome.getId().equals(originalJournalEntry.getGlAccount().getId())) {
+                            glAccountBalanceHolder.addToDebit(accountDeferredIncome, originalJournalEntry.getAmount());
+                        }
+                    }
+                }
+            } else {
+                LoanCapitalizedIncomeBalance capitalizedIncomeBalance = loanCapitalizedIncomeBalanceRepository
+                        .findBalanceForAdjustment(Long.parseLong(loanTransactionDTO.getTransactionId()));
+                if (MathUtil.isGreaterThan(transactionAmount, capitalizedIncomeBalance.getUnrecognizedAmount())) {
+                    BigDecimal amortizedAmount = transactionAmount.subtract(capitalizedIncomeBalance.getUnrecognizedAmount());
+
+                    glAccountBalanceHolder.addToDebit(accountIncomeFromCapitalization, amortizedAmount);
+                    glAccountBalanceHolder.addToDebit(accountDeferredIncome, transactionAmount.subtract(amortizedAmount));
+                } else {
+                    glAccountBalanceHolder.addToDebit(accountDeferredIncome, transactionAmount);
+                }
+            }
+        }
+
+        // create credit entries
+        for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
+            if (MathUtil.isGreaterThanZero(creditEntry.getValue())) {
+                GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
+                this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
+                        creditEntry.getValue(), glAccount);
+            }
+        }
+        // create debit entries
+        for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
+            if (MathUtil.isGreaterThanZero(debitEntry.getValue())) {
+                GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
+                this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
+                        debitEntry.getValue(), glAccount);
+            }
+        }
+    }
+
     private void createJournalEntriesForCapitalizedIncomeAmortization(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
             final Office office) {
         final boolean isMarkedAsChargeOff = loanDTO.isMarkedAsChargeOff();
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 89a1c58983..33e9da65e2 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
@@ -700,6 +700,8 @@ public class LoanTransactionsApiResource {
         CommandWrapper commandRequest;
         if (CommandParameterUtil.is(commandParam, LoanApiConstants.CHARGEBACK_TRANSACTION_COMMAND)) {
             commandRequest = builder.chargebackTransaction(resolvedLoanId, resolvedTransactionId).build();
+        } else if (CommandParameterUtil.is(commandParam, LoanApiConstants.CAPITALIZED_INCOME_ADJUSTMENT_TRANSACTION_COMMAND)) {
+            commandRequest = builder.capitalizedIncomeAdjustment(resolvedLoanId, resolvedTransactionId).build();
         } else { // Default to adjust the Loan Transaction
             commandRequest = builder.adjustTransaction(resolvedLoanId, resolvedTransactionId).build();
         }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index dfc38b76f7..39a37a81de 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -730,6 +730,8 @@ final class LoansApiResourceSwagger {
                 public boolean capitalizedIncome;
                 @Schema(example = "false")
                 public boolean capitalizedIncomeAmortization;
+                @Schema(example = "false")
+                public boolean capitalizedIncomeAdjustment;
             }
 
             static final class GetLoansLoanIdPaymentDetailData {
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index af37175c51..ce67fcb5da 100644
--- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -197,4 +197,5 @@
     <include file="parts/0176_add_capitalized_income_amortization_transaction.xml" relativeToChangelogFile="true" />
     <include file="parts/0177_acc_journal_entry_index.xml" relativeToChangelogFile="true" />
     <include file="parts/0178_add_principal_from_capitalized_income_to_loan_summary.xml" relativeToChangelogFile="true" />
+    <include file="parts/0179_add_capitalized_income_adjustment_transaction.xml" relativeToChangelogFile="true" />
 </databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0179_add_capitalized_income_adjustment_transaction.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0179_add_capitalized_income_adjustment_transaction.xml
new file mode 100644
index 0000000000..6005567757
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0179_add_capitalized_income_adjustment_transaction.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
+    <changeSet id="1" author="fineract">
+        <insert tableName="m_external_event_configuration">
+            <column name="type" value="LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent"/>
+            <column name="enabled" valueBoolean="false"/>
+        </insert>
+        <insert tableName="r_enum_value">
+            <column name="enum_name" value="transaction_type_enum"/>
+            <column name="enum_id" valueNumeric="37"/>
+            <column name="enum_message_property" value="Capitalized Income Adjustment"/>
+            <column name="enum_value" value="Capitalized Income Adjustment"/>
+            <column name="enum_type" valueBoolean="false"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java
index 00e59f0afd..141e406ac8 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/ExternalEventConfigurationValidationServiceTest.java
@@ -106,7 +106,8 @@ public class ExternalEventConfigurationValidationServiceTest {
                 "LoanTransactionInterestPaymentWaiverPostBusinessEvent", "LoanTransactionAccrualActivityPostBusinessEvent",
                 "LoanTransactionAccrualActivityPreBusinessEvent", "LoanTransactionInterestRefundPostBusinessEvent",
                 "LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent",
-                "LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent");
+                "LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
+                "LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent");
 
         List<FineractPlatformTenant> tenants = Arrays
                 .asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
@@ -193,7 +194,8 @@ public class ExternalEventConfigurationValidationServiceTest {
                 "LoanTransactionInterestPaymentWaiverPostBusinessEvent", "LoanTransactionAccrualActivityPostBusinessEvent",
                 "LoanTransactionAccrualActivityPreBusinessEvent", "LoanTransactionInterestRefundPostBusinessEvent",
                 "LoanTransactionInterestRefundPreBusinessEvent", "LoanAccrualAdjustmentTransactionBusinessEvent",
-                "LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent");
+                "LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent",
+                "LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent");
 
         List<FineractPlatformTenant> tenants = Arrays
                 .asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java
index 8d96e0b5ef..7e5ad02f8a 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java
@@ -19,12 +19,17 @@
 package org.apache.fineract.integrationtests;
 
 import java.math.BigDecimal;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReference;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdTransactions;
 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.PostLoansLoanIdTransactionsResponse;
 import org.apache.fineract.integrationtests.common.BusinessStepHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
@@ -96,4 +101,197 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
             );
         });
     }
+
+    @Test
+    public void testLoanCapitalizedIncomeAdjustment() {
+        final AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        final AtomicReference<Long> capitalizedIncomeIdRef = new AtomicReference<>();
+
+        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));
+
+        runAt("1 April 2024", () -> {
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
+                    500.0, 7.0, 3, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
+            PostLoansLoanIdTransactionsResponse capitalizedIncomeResponse = loanTransactionHelper.addCapitalizedIncome(loanId,
+                    "1 January 2024", 50.0);
+            capitalizedIncomeIdRef.set(capitalizedIncomeResponse.getResourceId());
+
+            loanTransactionHelper.capitalizedIncomeAdjustment(loanId, capitalizedIncomeIdRef.get(), "1 April 2024", 50.0);
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2024"), //
+                    transaction(50.0, "Capitalized Income", "01 January 2024"), //
+                    transaction(50.0, "Capitalized Income Adjustment", "01 April 2024") //
+            );
+
+            verifyJournalEntries(loanId, //
+                    journalEntry(100, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100, fundSource, "CREDIT"), //
+                    journalEntry(50, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT"), //
+                    journalEntry(50.0, deferredIncomeLiabilityAccount, "DEBIT"), //
+                    journalEntry(49.71, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(0.29, interestReceivableAccount, "CREDIT") //
+            );
+        });
+    }
+
+    @Test
+    public void testLoanCapitalizedIncomeAdjustmentValidations() {
+        final AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        final AtomicReference<Long> capitalizedIncomeIdRef = new AtomicReference<>();
+
+        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));
+
+        runAt("3 January 2024", () -> {
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
+                    500.0, 7.0, 3, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
+            PostLoansLoanIdTransactionsResponse capitalizedIncomeResponse = loanTransactionHelper.addCapitalizedIncome(loanId,
+                    "3 January 2024", 50.0);
+            capitalizedIncomeIdRef.set(capitalizedIncomeResponse.getResourceId());
+
+            // Amount more than remaining
+            Assertions.assertThrows(RuntimeException.class,
+                    () -> loanTransactionHelper.capitalizedIncomeAdjustment(loanId, capitalizedIncomeIdRef.get(), "3 January 2024", 60.0));
+
+            loanTransactionHelper.capitalizedIncomeAdjustment(loanId, capitalizedIncomeIdRef.get(), "3 January 2024", 30.0);
+            Assertions.assertThrows(RuntimeException.class,
+                    () -> loanTransactionHelper.capitalizedIncomeAdjustment(loanId, capitalizedIncomeIdRef.get(), "3 January 2024", 30.0));
+
+            // Capitalized income transaction with given id doesn't exist for this loan
+            Assertions.assertThrows(RuntimeException.class,
+                    () -> loanTransactionHelper.capitalizedIncomeAdjustment(loanId, 1L, "3 January 2024", 30.0));
+
+            // Cannot be earlier than capitalized income transaction
+            Assertions.assertThrows(RuntimeException.class,
+                    () -> loanTransactionHelper.capitalizedIncomeAdjustment(loanId, capitalizedIncomeIdRef.get(), "2 January 2024", 30.0));
+        });
+    }
+
+    @Test
+    public void testLoanCapitalizedIncomeAdjustmentWithAmortizationAccounting() {
+        final AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        final AtomicReference<Long> capitalizedIncomeIdRef = new AtomicReference<>();
+
+        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));
+
+        runAt("1 January 2024", () -> {
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
+                    500.0, 7.0, 3, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
+            PostLoansLoanIdTransactionsResponse capitalizedIncomeResponse = loanTransactionHelper.addCapitalizedIncome(loanId,
+                    "1 January 2024", 100.0);
+            capitalizedIncomeIdRef.set(capitalizedIncomeResponse.getResourceId());
+        });
+        runAt("2 January 2024", () -> {
+            Long loanId = loanIdRef.get();
+            executeInlineCOB(loanId);
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2024"), //
+                    transaction(100.0, "Capitalized Income", "01 January 2024"), //
+                    transaction(1.10, "Capitalized Income Amortization", "01 January 2024") //
+            );
+        });
+        runAt("3 January 2024", () -> {
+            Long loanId = loanIdRef.get();
+            executeInlineCOB(loanId);
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2024"), //
+                    transaction(100.0, "Capitalized Income", "01 January 2024"), //
+                    transaction(1.10, "Capitalized Income Amortization", "01 January 2024"), //
+                    transaction(0.04, "Accrual", "02 January 2024"), //
+                    transaction(1.10, "Capitalized Income Amortization", "02 January 2024") //
+            );
+
+            verifyJournalEntries(loanId, //
+                    journalEntry(100, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100, fundSource, "CREDIT"), //
+                    journalEntry(100, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100, deferredIncomeLiabilityAccount, "CREDIT"), //
+                    journalEntry(1.10, deferredIncomeLiabilityAccount, "DEBIT"), //
+                    journalEntry(1.10, feeIncomeAccount, "CREDIT"), //
+                    journalEntry(0.04, interestReceivableAccount, "DEBIT"), //
+                    journalEntry(0.04, interestIncomeAccount, "CREDIT"), //
+                    journalEntry(1.10, deferredIncomeLiabilityAccount, "DEBIT"), //
+                    journalEntry(1.10, feeIncomeAccount, "CREDIT") //
+            );
+
+            loanTransactionHelper.capitalizedIncomeAdjustment(loanId, capitalizedIncomeIdRef.get(), "3 January 2024", 100.0);
+
+            verifyTransactions(loanId, //
+                    transaction(100.0, "Disbursement", "01 January 2024"), //
+                    transaction(100.0, "Capitalized Income", "01 January 2024"), //
+                    transaction(1.10, "Capitalized Income Amortization", "01 January 2024"), //
+                    transaction(0.04, "Accrual", "02 January 2024"), //
+                    transaction(1.10, "Capitalized Income Amortization", "02 January 2024"), //
+                    transaction(100.0, "Capitalized Income Adjustment", "03 January 2024") //
+            );
+
+            verifyJournalEntries(loanId, //
+                    journalEntry(100, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100, fundSource, "CREDIT"), //
+                    journalEntry(100, loansReceivableAccount, "DEBIT"), //
+                    journalEntry(100, deferredIncomeLiabilityAccount, "CREDIT"), //
+                    journalEntry(1.10, deferredIncomeLiabilityAccount, "DEBIT"), //
+                    journalEntry(1.10, feeIncomeAccount, "CREDIT"), //
+                    journalEntry(0.04, interestReceivableAccount, "DEBIT"), //
+                    journalEntry(0.04, interestIncomeAccount, "CREDIT"), //
+                    journalEntry(1.10, deferredIncomeLiabilityAccount, "DEBIT"), //
+                    journalEntry(1.10, feeIncomeAccount, "CREDIT"), //
+                    journalEntry(99.92, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(0.08, interestReceivableAccount, "CREDIT"), //
+                    journalEntry(2.20, feeIncomeAccount, "DEBIT"), //
+                    journalEntry(97.80, deferredIncomeLiabilityAccount, "DEBIT") //
+            );
+
+            // Reverse-replay
+            addRepaymentForLoan(loanId, 67.45, "2 January 2024");
+
+            GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
+            Optional<GetLoansLoanIdTransactions> replayedCapitalizedIncomeAdjustmentOpt = loanDetails.getTransactions().stream()
+                    .filter(t -> t.getType().getCapitalizedIncomeAdjustment()).findFirst();
+            Assertions.assertTrue(replayedCapitalizedIncomeAdjustmentOpt.isPresent(), "Capitalized income adjustment not found");
+
+            verifyTRJournalEntries(replayedCapitalizedIncomeAdjustmentOpt.get().getId(), //
+                    journalEntry(99.98, loansReceivableAccount, "CREDIT"), //
+                    journalEntry(0.02, interestReceivableAccount, "CREDIT"), //
+                    journalEntry(2.20, feeIncomeAccount, "DEBIT"), //
+                    journalEntry(97.80, deferredIncomeLiabilityAccount, "DEBIT") //
+            );
+        });
+    }
 }
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 03f91075df..8682ba92ed 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
@@ -589,6 +589,12 @@ public class ExternalEventConfigurationHelper {
         loanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent.put("enabled", false);
         defaults.add(loanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent);
 
+        Map<String, Object> loanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent = new HashMap<>();
+        loanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent.put("type",
+                "LoanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent");
+        loanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent.put("enabled", false);
+        defaults.add(loanCapitalizedIncomeAdjustmentTransactionCreatedBusinessEvent);
+
         return defaults;
     }
 
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 b66700b9d6..da5059b6c8 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
@@ -933,6 +933,36 @@ public class LoanTransactionHelper {
                 new InterestPauseRequestDto().startDate(startDate).endDate(endDate).dateFormat(DATE_FORMAT).locale("en")));
     }
 
+    public PostLoansLoanIdTransactionsResponse capitalizedIncomeAdjustment(final Long loanId, final Long capitalizedIncomeTransactionId,
+            final PostLoansLoanIdTransactionsTransactionIdRequest request) {
+        return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction(loanId,
+                capitalizedIncomeTransactionId, request, "capitalizedIncomeAdjustment"));
+    }
+
+    public PostLoansLoanIdTransactionsResponse capitalizedIncomeAdjustment(final String loanExternalId, final Long transactionId,
+            final PostLoansLoanIdTransactionsTransactionIdRequest request) {
+        return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction2(loanExternalId, transactionId,
+                request, "capitalizedIncomeAdjustment"));
+    }
+
+    public PostLoansLoanIdTransactionsResponse capitalizedIncomeAdjustment(final String loanExternalId, final String transactionExternalId,
+            final PostLoansLoanIdTransactionsTransactionIdRequest request) {
+        return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction3(loanExternalId,
+                transactionExternalId, request, "capitalizedIncomeAdjustment"));
+    }
+
+    public PostLoansLoanIdTransactionsResponse capitalizedIncomeAdjustment(final Long loanId, final String transactionExternalId,
+            final PostLoansLoanIdTransactionsTransactionIdRequest request) {
+        return Calls.ok(FineractClientHelper.getFineractClient().loanTransactions.adjustLoanTransaction1(loanId, transactionExternalId,
+                request, "capitalizedIncomeAdjustment"));
+    }
+
+    public PostLoansLoanIdTransactionsResponse capitalizedIncomeAdjustment(final Long loanId, final Long capitalizedIncomeTransactionId,
+            final String transactionDate, final double amount) {
+        return capitalizedIncomeAdjustment(loanId, capitalizedIncomeTransactionId, new PostLoansLoanIdTransactionsTransactionIdRequest()
+                .transactionAmount(amount).transactionDate(transactionDate).dateFormat("dd MMMM yyyy").locale("en"));
+    }
+
     // TODO: Rewrite to use fineract-client instead!
     // Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
     // org.apache.fineract.client.models.PostLoansLoanIdRequest)