You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2024/12/13 15:48:12 UTC

(fineract) branch develop updated: FINERACT-2060: Progressive loan - final accrual calculation on closed loan

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

arnold 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 136bb0009 FINERACT-2060: Progressive loan - final accrual calculation on closed loan
136bb0009 is described below

commit 136bb0009ab3830045dbf0dfb09c7cde3a0162ba
Author: Marta Jankovics <ma...@gmail.com>
AuthorDate: Wed Dec 11 12:42:42 2024 +0100

    FINERACT-2060: Progressive loan - final accrual calculation on closed loan
---
 .../fineract/test/helper/ErrorMessageHelper.java   | 124 ++++-------
 .../test/stepdef/common/BatchApiStepDef.java       |   4 +-
 .../test/stepdef/common/JournalEntriesStepDef.java |   8 +-
 .../fineract/test/stepdef/loan/LoanStepDef.java    |  25 ++-
 .../test/resources/features/EMICalculation.feature |   2 +
 .../resources/features/LoanAccrualActivity.feature |  15 +-
 .../test/resources/features/LoanChargeback.feature |  12 +-
 .../test/resources/features/LoanRepayment.feature  |  10 +-
 .../loanaccount/data/AccrualChargeData.java        |   1 +
 .../loanaccount/data/AccrualPeriodData.java        |  10 +
 .../loanaccount/data/LoanScheduleAccrualData.java  | 201 -----------------
 .../portfolio/loanaccount/domain/Loan.java         |  11 +-
 .../LoanAccrualActivityProcessingService.java      |   9 +-
 ...AccrualTransactionBusinessEventServiceImpl.java |  10 +-
 .../service/LoanAccrualsProcessingService.java     |   8 +-
 .../service/LoanWritePlatformService.java          |  11 -
 .../domain/LoanAccountDomainServiceJpa.java        |  37 ++--
 .../AddPeriodicAccrualEntriesForLoansTasklet.java  |   2 +-
 ...nRescheduleRequestWritePlatformServiceImpl.java |   8 +-
 .../LoanAccrualActivityProcessingServiceImpl.java  | 221 +++++++++++--------
 ...rviceImpl.java => LoanAccrualEventService.java} |  45 ++--
 .../service/LoanAccrualsProcessingServiceImpl.java | 211 ++++++++----------
 .../LoanChargeWritePlatformServiceImpl.java        |  25 ++-
 .../LoanStatusChangePlatformServiceImpl.java       |  16 +-
 .../service/LoanTransactionAssembler.java          |  22 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java | 238 ++++++++-------------
 .../starter/LoanAccountConfiguration.java          |  37 ++--
 .../LoanChargeWritePlatformServiceImplTest.java    |   3 +
 ...nWritePlatformServiceJpaRepositoryImplTest.java |   4 -
 .../integrationtests/BaseLoanIntegrationTest.java  |  13 +-
 .../LoanChargeProgressiveTest.java                 |  12 +-
 ...ebackWithCreditAllocationsIntegrationTests.java |   4 +
 .../integrationtests/LoanInterestRefundTest.java   |  69 +++---
 .../LoanTransactionAccrualActivityPostingTest.java |  64 +++---
 34 files changed, 628 insertions(+), 864 deletions(-)

diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
index 08bd21ff5..4bc6dbdfe 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java
@@ -348,9 +348,12 @@ public final class ErrorMessageHelper {
     }
 
     public static String wrongLoanStatus(Integer actual, Integer expected) {
-        String actualToStr = actual.toString();
-        String expectedToStr = expected.toString();
-        return String.format("Wrong Loan status ID. Actual ID is: %s - But expected ID is: %s", actualToStr, expectedToStr);
+        return wrongLoanStatus(null, actual, expected);
+    }
+
+    public static String wrongLoanStatus(String resourceId, Integer actual, Integer expected) {
+        return String.format("Wrong Loan status ID of resource %s. Actual ID is: %s - But expected ID is: %s", resourceId, actual,
+                expected);
     }
 
     public static String wrongFraudFlag(Boolean actualFraudStatus, Boolean expectedFraudStatus) {
@@ -446,109 +449,54 @@ public final class ErrorMessageHelper {
         return String.format("Idempotency key is not matching:  Actual value is: %s - But expected value is: %s", actual, expected);
     }
 
-    public static String wrongNumberOfLinesInRepaymentSchedule(int actual, int expected) {
-        return wrongNumberOfLinesInRepaymentSchedule(null, actual, expected);
-    }
-
     public static String wrongNumberOfLinesInRepaymentSchedule(String resourceId, int actual, int expected) {
-        String actualStr = String.valueOf(actual);
-        String expectedStr = String.valueOf(expected);
-        String prefx = "Number of lines in Repayment schedule";
-        String postfx = "  is not correct. Actual value is: %s - Expected value is: %s";
-        if (resourceId != null) {
-            return String.format(prefx + " of resource %s" + postfx, resourceId, actualStr, expectedStr);
-        }
-        return String.format(prefx + postfx, actualStr, expectedStr);
-    }
-
-    public static String wrongValueInLineInRepaymentSchedule(int line, List<List<String>> actual, List<String> expected) {
-        return wrongValueInLineInRepaymentSchedule(null, line, actual, expected);
+        return String.format("Number of lines in Repayment schedule of resource %s is not correct. " //
+                + "Actual value is: %s - But expected value is: %s", resourceId, actual, expected);
     }
 
-    public static String wrongValueInLineInRepaymentSchedule(String resourceId, int line, List<List<String>> actual,
+    public static String wrongValueInLineInRepaymentSchedule(String resourceId, int line, List<List<String>> actualList,
             List<String> expected) {
-        String lineStr = String.valueOf(line);
-        String expectedStr = expected.toString();
-        StringBuilder sb = new StringBuilder();
-        for (List<String> innerList : actual) {
-            sb.append(innerList.toString());
-            sb.append(System.lineSeparator());
-        }
-        String prefx = "%nWrong value in Repayment schedule";
-        String postfx = " tab line %s. %nActual values in line (with the same due date) are: %n%s %nExpected values in line: %n%s";
-        if (resourceId != null) {
-            return String.format(prefx + " of resource %s" + postfx, resourceId, lineStr, sb.toString(), expectedStr);
-        }
-        return String.format(prefx + postfx, lineStr, sb.toString(), expectedStr);
-    }
-
-    public static String wrongValueInLineInTransactionsTab(int line, List<List<String>> actual, List<String> expected) {
-        return wrongValueInLineInTransactionsTab(null, line, actual, expected);
-    }
-
-    public static String wrongValueInLineInTransactionsTab(String resourceId, int line, List<List<String>> actual, List<String> expected) {
-        String lineStr = String.valueOf(line);
-        String expectedStr = expected.toString();
-        StringBuilder sb = new StringBuilder();
-        for (List<String> innerList : actual) {
-            sb.append(innerList.toString());
-            sb.append(System.lineSeparator());
-        }
-        String prefx = "%nWrong value in Transactions tab";
-        String postfx = " line %s. %nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s";
-        if (resourceId != null) {
-            return String.format(prefx + " of resource %s" + postfx, resourceId, lineStr, sb.toString(), expectedStr);
-        }
-        return String.format(prefx + postfx, lineStr, sb.toString(), expectedStr);
+        String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+        return String.format("%nWrong value in Repayment schedule of resource %s tab line %s." //
+                + "%nActual values in line (with the same due date) are: %n%s - But expected values in line: %n%s", resourceId, line,
+                actual, expected);
     }
 
-    public static String nrOfLinesWrongInTransactionsTab(int actual, int expected) {
-        return nrOfLinesWrongInTransactionsTab(null, actual, expected);
+    public static String wrongValueInLineInTransactionsTab(String resourceId, int line, List<List<String>> actualList,
+            List<String> expected) {
+        String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+        return String.format("%nWrong value in Transactions tab of resource %s line %s." //
+                + "%nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s", resourceId, line, actual,
+                expected);
     }
 
     public static String nrOfLinesWrongInTransactionsTab(String resourceId, int actual, int expected) {
-        String actualStr = String.valueOf(actual);
-        String expectedStr = String.valueOf(expected);
-
-        String prefx = "%nNumber of lines does not match in Transactions tab and expected datatable";
-        String postfx = ". %nNumber of transaction tab lines: %s %nNumber of expected datatable lines: %s%n";
-        if (resourceId != null) {
-            return String.format(prefx + " of resource %s" + postfx, resourceId, actualStr, expectedStr);
-        }
-        return String.format(prefx + postfx, actualStr, expectedStr);
+        return String.format("%nNumber of lines does not match in Transactions tab and expected datatable of resource %s." //
+                + "%nNumber of transaction tab lines: %s %nNumber of expected datatable lines: %s%n", resourceId, actual, expected);
     }
 
-    public static String wrongValueInLineInChargesTab(int line, List<List<String>> actual, List<String> expected) {
-        String lineStr = String.valueOf(line);
-        String expectedStr = expected.toString();
-        StringBuilder sb = new StringBuilder();
-        for (List<String> innerList : actual) {
-            sb.append(innerList.toString());
-            sb.append(System.lineSeparator());
-        }
-
-        return String.format(
-                "%nWrong value in Charges tab line %s. %nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s",
-                lineStr, sb.toString(), expectedStr);
+    public static String wrongValueInLineInChargesTab(String resourceId, int line, List<List<String>> actualList, List<String> expected) {
+        String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+        return String.format("%nWrong value in Charges tab of resource %s line %s." //
+                + "%nActual values in line (with the same date) are: %n%s %nExpected values in line: %n%s", resourceId, line, actual,
+                expected);
     }
 
     public static String wrongValueInLineInJournalEntries(int line, List<List<List<String>>> actual, List<String> expected) {
-        String lineStr = String.valueOf(line);
-        String expectedStr = expected.toString();
-        StringBuilder sb = new StringBuilder();
-        for (List<List<String>> innerList : actual) {
-            sb.append(innerList.toString());
-            sb.append(System.lineSeparator());
-        }
+        return wrongValueInLineInJournalEntries(null, line, actual, expected);
+    }
 
-        return String.format(
-                "%nWrong value in Journal entries line %s. %nActual values for the possible transactions in line (with the same date) are: %n%s %nExpected values in line: %n%s",
-                lineStr, sb.toString(), expectedStr);
+    public static String wrongValueInLineInJournalEntries(String resourceId, int line, List<List<List<String>>> actualList,
+            List<String> expected) {
+        String actual = actualList.stream().map(Object::toString).collect(Collectors.joining(System.lineSeparator()));
+        return String.format("%nWrong value in Journal entries of resource %s line %s." //
+                + "%nActual values for the possible transactions in line (with the same date) are: %n%s %nExpected values in line: %n%s",
+                resourceId, line, actual, expected);
     }
 
     public static String wrongDataInJournalEntriesGlAccountType(int line, String actual, String expected) {
-        return String.format("Wrong data in Journal entries, line %s / GL account type. Actual value is: %s - But expected value is: %s",
-                line, actual, expected);
+        return String.format("Wrong data in Journal entries, line %s / GL account type. " //
+                + "Actual value is: %s - But expected value is: %s", line, actual, expected);
     }
 
     public static String wrongDataInJournalEntriesGlAccountCode(int line, String actual, String expected) {
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java
index e5d611bea..6f80c2b68 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/BatchApiStepDef.java
@@ -865,7 +865,9 @@ public class BatchApiStepDef extends AbstractStepDef {
         Integer statusIdActual = status.getId();
         Integer statusIdExpected = LoanStatus.APPROVED.value;
 
-        assertThat(statusIdActual).as(ErrorMessageHelper.wrongLoanStatus(statusIdActual, statusIdExpected)).isEqualTo(statusIdExpected);
+        String resourceId = String.valueOf(response.body().getId());
+        assertThat(statusIdActual).as(ErrorMessageHelper.wrongLoanStatus(resourceId, statusIdActual, statusIdExpected))
+                .isEqualTo(statusIdExpected);
     }
 
     @Then("Nr. {int} Client creation was rolled back")
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java
index 97191a40a..f2f74e20d 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/common/JournalEntriesStepDef.java
@@ -59,6 +59,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
         Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
 
         Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
         ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -132,7 +133,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
                 }
             }
             assertThat(containsAnyExpected)
-                    .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(i, possibleActualValuesList, expectedValues)).isTrue();
+                    .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(resourceId, i, possibleActualValuesList, expectedValues))
+                    .isTrue();
         }
     }
 
@@ -141,6 +143,7 @@ public class JournalEntriesStepDef extends AbstractStepDef {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
         Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
 
         Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
         ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -218,7 +221,8 @@ public class JournalEntriesStepDef extends AbstractStepDef {
                 }
             }
             assertThat(containsAnyExpected)
-                    .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(i, possibleActualValuesList, expectedValues)).isTrue();
+                    .as(ErrorMessageHelper.wrongValueInLineInJournalEntries(resourceId, i, possibleActualValuesList, expectedValues))
+                    .isTrue();
         }
     }
 }
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
index 20d50d2af..e1b4cdcb9 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
@@ -1298,6 +1298,8 @@ public class LoanStepDef extends AbstractStepDef {
     public void disburseLoan(String actualDisbursementDate, String transactionAmount) throws IOException {
         Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
+
         PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
                 .actualDisbursementDate(actualDisbursementDate).transactionAmount(new BigDecimal(transactionAmount));
 
@@ -1310,7 +1312,7 @@ public class LoanStepDef extends AbstractStepDef {
         Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
 
         assertThat(statusActual)//
-                .as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
+                .as(ErrorMessageHelper.wrongLoanStatus(resourceId, Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
                 .isEqualTo(statusExpected);//
         eventCheckHelper.disburseLoanEventCheck(loanId);
         eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
@@ -1320,6 +1322,8 @@ public class LoanStepDef extends AbstractStepDef {
     public void disburseLoanWithoutAutoDownpayment(String actualDisbursementDate, String transactionAmount) throws IOException {
         Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
+
         PostLoansLoanIdRequest disburseRequest = LoanRequestFactory.defaultLoanDisburseRequest()
                 .actualDisbursementDate(actualDisbursementDate).transactionAmount(new BigDecimal(transactionAmount));
 
@@ -1333,7 +1337,7 @@ public class LoanStepDef extends AbstractStepDef {
         Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
 
         assertThat(statusActual)//
-                .as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
+                .as(ErrorMessageHelper.wrongLoanStatus(resourceId, Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))//
                 .isEqualTo(statusExpected);//
         eventCheckHelper.disburseLoanEventCheck(loanId);
         eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
@@ -1773,6 +1777,7 @@ public class LoanStepDef extends AbstractStepDef {
     public void loanChargesGivenChargeDataCheck(DataTable table) throws IOException {
         Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanCreateResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
 
         Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
         ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -1786,14 +1791,15 @@ public class LoanStepDef extends AbstractStepDef {
 
         boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
 
-        assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInChargesTab(1, actualValuesList, expectedValues))
-                .isTrue();
+        assertThat(containsExpectedValues)
+                .as(ErrorMessageHelper.wrongValueInLineInChargesTab(resourceId, 1, actualValuesList, expectedValues)).isTrue();
     }
 
     @Then("Loan Charges tab has the following data:")
     public void loanChargesTabCheck(DataTable table) throws IOException {
         Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanCreateResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
 
         Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "charges", "", "").execute();
         ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -1808,8 +1814,8 @@ public class LoanStepDef extends AbstractStepDef {
 
             boolean containsExpectedValues = actualValuesList.stream().anyMatch(actualValues -> actualValues.equals(expectedValues));
 
-            assertThat(containsExpectedValues).as(ErrorMessageHelper.wrongValueInLineInChargesTab(i, actualValuesList, expectedValues))
-                    .isTrue();
+            assertThat(containsExpectedValues)
+                    .as(ErrorMessageHelper.wrongValueInLineInChargesTab(resourceId, i, actualValuesList, expectedValues)).isTrue();
         }
     }
 
@@ -1846,6 +1852,7 @@ public class LoanStepDef extends AbstractStepDef {
     public void loanStatus(String statusExpected) throws IOException {
         Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
         long loanId = loanCreateResponse.body().getLoanId();
+        String resourceId = String.valueOf(loanId);
 
         Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "", "", "").execute();
         ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
@@ -1855,7 +1862,7 @@ public class LoanStepDef extends AbstractStepDef {
         LoanStatus loanStatusExpected = LoanStatus.valueOf(statusExpected);
         Integer loanStatusExpectedValue = loanStatusExpected.getValue();
 
-        assertThat(loanStatusActualValue).as(ErrorMessageHelper.wrongLoanStatus(loanStatusActualValue, loanStatusExpectedValue))
+        assertThat(loanStatusActualValue).as(ErrorMessageHelper.wrongLoanStatus(resourceId, loanStatusActualValue, loanStatusExpectedValue))
                 .isEqualTo(loanStatusExpectedValue);
     }
 
@@ -2776,7 +2783,9 @@ public class LoanStepDef extends AbstractStepDef {
         assertNotNull(loanDetails.body().getStatus());
         final Long statusExpected = Long.valueOf(loanDetails.body().getStatus().getId());
 
-        assertThat(statusActual).as(ErrorMessageHelper.wrongLoanStatus(Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))
+        String resourceId = String.valueOf(loanId);
+        assertThat(statusActual)
+                .as(ErrorMessageHelper.wrongLoanStatus(resourceId, Math.toIntExact(statusActual), Math.toIntExact(statusExpected)))
                 .isEqualTo(statusExpected);
         eventCheckHelper.disburseLoanEventCheck(loanId);
         eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature b/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature
index ece05f178..3966682d8 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/EMICalculation.feature
@@ -5231,6 +5231,7 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
       | 22 January 2021  | Accrual                | 5.7     | 0.0       | 5.7      | 0.0  | 0.0       | 0.0          | false    | false    |
       | 22 January 2021  | Merchant Issued Refund | 1000.0  | 914.37    | 5.42     | 0.0  | 0.0       | 0.0          | false    | true     |
       | 22 January 2021  | Interest Refund        | 5.42    | 0.0       | 0.0      | 0.0  | 0.0       | 0.0          | false    | true     |
+      | 22 January 2021  | Accrual Adjustment     | 0.28    | 0.0       | 0.28     | 0.0  | 0.0       | 0.0          | false    | false    |
 
   @TestRailId:C3302
   Scenario: UC18-2 - In case of repayment reversal the Interest Refund transaction needs to be recalculated
@@ -5288,6 +5289,7 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
       | 22 January 2021  | Merchant Issued Refund | 1000.0  | 1000.0    | 0.0      | 0.0  | 0.0       | 0.0          | false    | true     |
       | 22 January 2021  | Interest Refund        | 5.7     | 0.0       | 5.7      | 0.0  | 0.0       | 0.0          | false    | true     |
       | 23 January 2021  | Credit Balance Refund  | 85.63   | 85.63     | 0.0      | 0.0  | 0.0       | 85.63        | false    | true     |
+      | 22 January 2021  | Accrual                | 5.42    | 0.0       | 5.42     | 0.0  | 0.0       | 0.0          | false    | false    |
     And In Loan Transactions the "2"th Transaction has Transaction type="Repayment" and is reverted
 
   @TestRailId:C3303
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature
index 5bf616d7d..c16a4f24c 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanAccrualActivity.feature
@@ -1331,10 +1331,8 @@ Feature: LoanAccrualActivity
     Then Loan Transactions tab has the following data:
       | Transaction date | Transaction Type   | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
       | 01 January 2024  | Disbursement       | 1000.0 | 0.0       | 0.0      | 0.0  | 0.0       | 1000.0       | false    | false    |
-      | 02 January 2024  | Accrual            | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
-      | 03 January 2024  | Accrual            | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
       | 04 January 2024  | Repayment          | 1004.1 | 1000.0    | 4.1      | 0.0  | 0.0       | 0.0          | true     | false    |
-      | 04 January 2024  | Accrual            | 0.32   | 0.0       | 0.32     | 0.0  | 0.0       | 0.0          | false    | false    |
+      | 04 January 2024  | Accrual            | 4.1    | 0.0       | 4.1      | 0.0  | 0.0       | 0.0          | false    | false    |
 #    --- Accrual activity ---
     When Admin sets the business date to "07 January 2024"
     When Admin runs inline COB job for Loan
@@ -1351,11 +1349,9 @@ Feature: LoanAccrualActivity
     Then Loan Transactions tab has the following data:
       | Transaction date | Transaction Type   | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
       | 01 January 2024  | Disbursement       | 1000.0 | 0.0       | 0.0      | 0.0  | 0.0       | 1000.0       | false    | false    |
-      | 02 January 2024  | Accrual            | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
-      | 03 January 2024  | Accrual            | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
+      | 04 January 2024  | Accrual            | 4.1    | 0.0       | 4.1      | 0.0  | 0.0       | 0.0          | false    | false    |
       | 04 January 2024  | Repayment          | 1004.1 | 1000.0    | 4.1      | 0.0  | 0.0       | 0.0          | true     | false    |
-      | 04 January 2024  | Accrual            | 0.32   | 0.0       | 0.32     | 0.0  | 0.0       | 0.0          | false    | false    |
-      | 05 January 2024  | Accrual            | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
+      | 05 January 2024  | Accrual Adjustment | 2.79   | 0.0       | 2.79     | 0.0  | 0.0       | 0.0          | false    | false    |
       | 06 January 2024  | Accrual            | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
       | 06 January 2024  | Accrual Activity   | 1.64   | 0.0       | 1.64     | 0.0  | 0.0       | 0.0          | false    | false    |
 
@@ -1481,9 +1477,8 @@ Feature: LoanAccrualActivity
       | 05 January 2024  | Accrual          | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
       | 06 January 2024  | Accrual          | 0.33   | 0.0       | 0.33     | 0.0  | 0.0       | 0.0          | false    | false    |
       | 06 January 2024  | Accrual Activity | 1.64   | 0.0       | 1.64     | 0.0  | 0.0       | 0.0          | false    | false    |
-      | 07 January 2024  | Accrual          | 0.25   | 0.0       | 0.25     | 0.0  | 0.0       | 0.0          | false    | false    |
       | 08 January 2024  | Repayment        | 753.07 | 749.4     | 3.67     | 0.0  | 0.0       | 250.6        | false    | true     |
-      | 08 January 2024  | Accrual          | 0.24   | 0.0       | 0.24     | 0.0  | 0.0       | 0.0          | false    | false    |
+      | 08 January 2024  | Accrual          | 2.46   | 0.0       | 2.46     | 0.0  | 0.0       | 0.0          | false    | false    |
 
   @TestRailId:C3182
   Scenario: Verify accrual activity posting job
@@ -2045,9 +2040,9 @@ Feature: LoanAccrualActivity
     Then Loan Transactions tab has the following data:
       | Transaction date | Transaction Type  | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
       | 22 April 2024    | Disbursement      | 400.0  | 0.0       | 0.0      | 0.0  | 0.0       | 400.0        | false    | false    |
+      | 22 April 2024    | Accrual           | 30.0   | 0.0       | 0.0      | 0.0  | 30.0      | 0.0          | false    | false    |
       | 22 April 2024    | Repayment         | 600.0  | 400.0     | 0.0      | 0.0  | 30.0      | 0.0          | true     | true     |
       | 22 April 2024    | Goodwill Credit   | 15.0   | 0.0       | 0.0      | 0.0  | 15.0      | 400.0        | false    | true     |
-      | 09 October 2024  | Accrual           | 30.0   | 0.0       | 0.0      | 0.0  | 30.0      | 0.0          | false    | false    |
       | 10 October 2024  | Charge Adjustment | 15.0   | 0.0       | 0.0      | 0.0  | 15.0      | 400.0        | false    | true     |
 
   @TestRailId:C3274
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature
index ee224ea57..48827bb9b 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanChargeback.feature
@@ -2959,6 +2959,7 @@ Feature: LoanChargeback
       | 01 January 2024  | Disbursement           | 100.0  | 0.0       | 0.0      | 0.0  | 0.0       | 100.0        | 0.0         |
       | 19 January 2024  | Merchant Issued Refund | 10.0   | 10.0      | 0.0      | 0.0  | 0.0       | 90.0         | 0.0         |
       | 01 February 2024 | Repayment              | 105.0  | 90.0      | 0.0      | 0.0  | 5.0       | 0.0          | 10.0        |
+      | 01 February 2024 | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 15 April 2024    | Chargeback             | 7.0    | 2.0       | 0.0      | 0.0  | 5.0       | 0.0          | 7.0         |
       | 16 April 2024    | Chargeback             | 7.0    | 7.0       | 0.0      | 0.0  | 0.0       | 4.0          | 3.0         |
     Then Loan Transactions tab has a "CHARGEBACK" transaction with date "16 April 2024" which has the following Journal entries:
@@ -3032,6 +3033,7 @@ Feature: LoanChargeback
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
       | 01 January 2024  | Disbursement     | 100.0  | 0.0       | 0.0      | 0.0  | 0.0       | 100.0        | 0.0         |
       | 01 February 2024 | Repayment        | 105.0  | 100.0     | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
+      | 01 February 2024 | Accrual          | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 01 March 2024    | Chargeback       | 3.0    | 0.0       | 0.0      | 0.0  | 3.0       | 0.0          | 0.0         |
     Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
       | Type      | Account code | Account name              | Debit | Credit |
@@ -3065,6 +3067,7 @@ Feature: LoanChargeback
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
       | 01 January 2024  | Disbursement     | 100.0  | 0.0       | 0.0      | 0.0  | 0.0       | 100.0        | 0.0         |
       | 01 February 2024 | Repayment        | 105.0  | 100.0     | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
+      | 01 February 2024 | Accrual          | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 01 March 2024    | Chargeback       | 3.0    | 0.0       | 0.0      | 0.0  | 3.0       | 0.0          | 0.0         |
       | 05 March 2024    | Chargeback       | 10.0   | 8.0       | 0.0      | 0.0  | 2.0       | 8.0          | 0.0         |
     Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
@@ -3154,8 +3157,8 @@ Feature: LoanChargeback
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
       | 01 January 2024  | Disbursement     | 100.0  | 0.0       | 0.0      | 0.0  | 0.0       | 100.0        | 0.0         |
       | 01 February 2024 | Repayment        | 105.0  | 100.0     | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
+      | 01 February 2024 | Accrual          | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 15 April 2024    | Chargeback       | 3.0    | 0.0       | 0.0      | 0.0  | 3.0       | 0.0          | 0.0         |
-      | 15 April 2024    | Accrual          | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
     Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
       | Type      | Account code | Account name              | Debit | Credit |
       | ASSET     | 112601       | Loans Receivable          | 100.0 |        |
@@ -3165,7 +3168,7 @@ Feature: LoanChargeback
       | ASSET     | 112601       | Loans Receivable          |       | 100.0  |
       | ASSET     | 112603       | Interest/Fee Receivable   |       | 5.0    |
       | LIABILITY | 145023       | Suspense/Clearing account | 105.0 |        |
-    Then Loan Transactions tab has a "ACCRUAL" transaction with date "15 April 2024" which has the following Journal entries:
+    Then Loan Transactions tab has a "ACCRUAL" transaction with date "01 February 2024" which has the following Journal entries:
       | Type   | Account code | Account name            | Debit | Credit |
       | ASSET  | 112603       | Interest/Fee Receivable | 5.0   |        |
       | INCOME | 404007       | Fee Income              |       | 5.0    |
@@ -3193,8 +3196,8 @@ Feature: LoanChargeback
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Overpayment |
       | 01 January 2024  | Disbursement     | 100.0  | 0.0       | 0.0      | 0.0  | 0.0       | 100.0        | 0.0         |
       | 01 February 2024 | Repayment        | 105.0  | 100.0     | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
+      | 01 February 2024 | Accrual          | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 15 April 2024    | Chargeback       | 3.0    | 0.0       | 0.0      | 0.0  | 3.0       | 0.0          | 0.0         |
-      | 15 April 2024    | Accrual          | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 20 April 2024    | Chargeback       | 10.0   | 8.0       | 0.0      | 0.0  | 2.0       | 8.0          | 0.0         |
     Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
       | Type      | Account code | Account name              | Debit | Credit |
@@ -3205,7 +3208,7 @@ Feature: LoanChargeback
       | ASSET     | 112601       | Loans Receivable          |       | 100.0  |
       | ASSET     | 112603       | Interest/Fee Receivable   |       | 5.0    |
       | LIABILITY | 145023       | Suspense/Clearing account | 105.0 |        |
-    Then Loan Transactions tab has a "ACCRUAL" transaction with date "15 April 2024" which has the following Journal entries:
+    Then Loan Transactions tab has a "ACCRUAL" transaction with date "01 February 2024" which has the following Journal entries:
       | Type   | Account code | Account name            | Debit | Credit |
       | ASSET  | 112603       | Interest/Fee Receivable | 5.0   |        |
       | INCOME | 404007       | Fee Income              |       | 5.0    |
@@ -3354,6 +3357,7 @@ Feature: LoanChargeback
       | 01 January 2024  | Disbursement           | 100.0  | 0.0       | 0.0      | 0.0  | 0.0       | 100.0        | 0.0         |
       | 19 January 2024  | Merchant Issued Refund | 10.0   | 10.0      | 0.0      | 0.0  | 0.0       | 90.0         | 0.0         |
       | 01 February 2024 | Repayment              | 105.0  | 90.0      | 0.0      | 0.0  | 5.0       | 0.0          | 10.0        |
+      | 01 February 2024 | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | 0.0         |
       | 15 April 2024    | Chargeback             | 3.0    | 0.0       | 0.0      | 0.0  | 3.0       | 0.0          | 3.0         |
       | 20 April 2024    | Chargeback             | 10.0   | 8.0       | 0.0      | 0.0  | 2.0       | 3.0          | 7.0         |
     Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2024" which has the following Journal entries:
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
index 4e206c9a8..aebca854c 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature
@@ -2473,8 +2473,8 @@ Feature: LoanRepayment
     Then Loan Transactions tab has the following data:
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
       | 01 January 2023  | Disbursement     | 1000.0 | 0.0       | 0.0      | 0.0  | 0.0       | 1000.0       |
-      | 10 January 2023  | Repayment        | 1020.0 | 1000.0    | 0.0      | 20.0 | 0.0       | 0.0          |
       | 10 January 2023  | Accrual          | 20.0   | 0.0       | 0.0      | 20.0 | 0.0       | 0.0          |
+      | 10 January 2023  | Repayment        | 1020.0 | 1000.0    | 0.0      | 20.0 | 0.0       | 0.0          |
     Then Loan Charges tab has the following data:
       | Name       | isPenalty | Payment due at     | Due as of        | Calculation type | Due  | Paid | Waived | Outstanding |
       | NSF fee    | true      | Specified due date | 28 January 2023  | Flat             | 20.0 | 0.0  | 0.0    | 20.0        |
@@ -2486,8 +2486,8 @@ Feature: LoanRepayment
     Then Loan Transactions tab has the following data:
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
       | 01 January 2023  | Disbursement     | 1000.0 | 0.0       | 0.0      | 0.0  | 0.0       | 1000.0       |
-      | 10 January 2023  | Repayment        | 1020.0 | 1000.0    | 0.0      | 20.0 | 0.0       | 0.0          |
       | 10 January 2023  | Accrual          | 20.0   | 0.0       | 0.0      | 20.0 | 0.0       | 0.0          |
+      | 10 January 2023  | Repayment        | 1020.0 | 1000.0    | 0.0      | 20.0 | 0.0       | 0.0          |
       | 28 January 2023  | Repayment        | 520.0  | 500.0     | 0.0      | 0.0  | 20.0      | 500.0        |
     Then Loan Charges tab has the following data:
       | Name       | isPenalty | Payment due at     | Due as of        | Calculation type | Due  | Paid | Waived | Outstanding |
@@ -2507,8 +2507,8 @@ Feature: LoanRepayment
     Then Loan Transactions tab has the following data:
       | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
       | 01 January 2023  | Disbursement     | 1000.0 | 0.0       | 0.0      | 0.0  | 0.0       | 1000.0       |
-      | 10 January 2023  | Repayment        | 1020.0 | 1000.0    | 0.0      | 20.0 | 0.0       | 0.0          |
       | 10 January 2023  | Accrual          | 20.0   | 0.0       | 0.0      | 20.0 | 0.0       | 0.0          |
+      | 10 January 2023  | Repayment        | 1020.0 | 1000.0    | 0.0      | 20.0 | 0.0       | 0.0          |
       | 28 January 2023  | Repayment        | 520.0  | 500.0     | 0.0      | 0.0  | 20.0      | 500.0        |
       | 30 January 2023  | Repayment        | 520.0  | 500.0     | 0.0      | 20.0 | 0.0       | 0.0          |
       | 30 January 2023  | Accrual          | 20.0   | 0.0       | 0.0      | 0.0  | 20.0      | 0.0          |
@@ -3656,9 +3656,9 @@ Feature: LoanRepayment
       | 13 August 2024   | Repayment              | 35.44  | 35.44     | 0.0      | 0.0  | 0.0       | 0.0          | true     |
       | 22 August 2024   | Repayment              | 35.44  | 35.44     | 0.0      | 0.0  | 0.0       | 0.0          | true     |
       | 22 August 2024   | Repayment              | 38.24  | 35.44     | 0.0      | 0.0  | 2.8       | 0.0          | false    |
-      | 22 August 2024   | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | false    |
       | 23 August 2024   | Repayment              | 10.0   | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | false    |
       | 24 August 2024   | Accrual                | 2.8    | 0.0       | 0.0      | 0.0  | 2.8       | 0.0          | false    |
+      | 24 August 2024   | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | false    |
     Then Loan Repayment schedule has 2 periods, with the following data for periods:
       | Nr | Days | Date           | Paid date      | Balance of loan | Principal due | Interest | Fees | Penalties | Due    | Paid   | In advance | Late | Outstanding |
       |    |      | 23 July 2024   |                | 111.92          |               |          | 0.0  |           | 0.0    | 0.0    |            |      |             |
@@ -3676,9 +3676,9 @@ Feature: LoanRepayment
       | 13 August 2024   | Repayment              | 35.44  | 35.44     | 0.0      | 0.0  | 0.0       | 0.0          | true     |
       | 22 August 2024   | Repayment              | 35.44  | 35.44     | 0.0      | 0.0  | 0.0       | 0.0          | true     |
       | 22 August 2024   | Repayment              | 38.24  | 35.44     | 0.0      | 0.0  | 2.8       | 0.0          | false    |
-      | 22 August 2024   | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | false    |
       | 23 August 2024   | Repayment              | 10.0   | 0.0       | 0.0      | 0.0  | 10.0      | 0.0          | false    |
       | 24 August 2024   | Accrual                | 2.8    | 0.0       | 0.0      | 0.0  | 2.8       | 0.0          | false    |
+      | 24 August 2024   | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | false    |
       | 25 August 2024   | Accrual                | 5.0    | 0.0       | 0.0      | 0.0  | 5.0       | 0.0          | false    |
     Then Loan Repayment schedule has 2 periods, with the following data for periods:
       | Nr | Days | Date           | Paid date      | Balance of loan | Principal due | Interest | Fees | Penalties | Due    | Paid   | In advance | Late | Outstanding |
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java
index d3109bd7e..447131c05 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualChargeData.java
@@ -34,4 +34,5 @@ public class AccrualChargeData {
     private Money chargeAmount;
     private Money chargeAccruable;
     private Money chargeAccrued;
+    private Money transactionAccrued;
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java
index 7c39384eb..30252632a 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/AccrualPeriodData.java
@@ -68,10 +68,20 @@ public class AccrualPeriodData {
         return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
     }
 
+    public Money getFeeTransactionAccrued() {
+        return charges.stream().filter(charge -> !charge.isPenalty()).map(AccrualChargeData::getTransactionAccrued).reduce(null,
+                MathUtil::plus);
+    }
+
     public Money getPenaltyAccrued() {
         return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getChargeAccrued).reduce(null, MathUtil::plus);
     }
 
+    public Money getPenaltyTransactionAccrued() {
+        return charges.stream().filter(AccrualChargeData::isPenalty).map(AccrualChargeData::getTransactionAccrued).reduce(null,
+                MathUtil::plus);
+    }
+
     public Money getChargeAccruable() {
         return charges.stream().map(AccrualChargeData::getChargeAccruable).reduce(null, MathUtil::plus);
     }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleAccrualData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleAccrualData.java
deleted file mode 100644
index 21abaa153..000000000
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanScheduleAccrualData.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * 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.data;
-
-import java.math.BigDecimal;
-import java.time.LocalDate;
-import java.util.Map;
-import org.apache.fineract.organisation.monetary.data.CurrencyData;
-import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
-
-public class LoanScheduleAccrualData {
-
-    private final Long loanId;
-    private final Long officeId;
-    private final LocalDate accruedTill;
-    private final PeriodFrequencyType repaymentFrequency;
-    private final Integer repayEvery;
-    private final Integer installmentNumber;
-    private final LocalDate dueDate;
-    private final LocalDate fromDate;
-    private final Long repaymentScheduleId;
-    private final Long loanProductId;
-    private final BigDecimal interestIncome;
-    private final BigDecimal feeIncome;
-    private final BigDecimal penaltyIncome;
-    private final BigDecimal waivedInterestIncome;
-    private final BigDecimal accruedInterestIncome;
-    private final BigDecimal accruedFeeIncome;
-    private final BigDecimal accruedPenaltyIncome;
-    private final CurrencyData currencyData;
-    private final LocalDate interestCalculatedFrom;
-
-    private Map<LoanChargeData, BigDecimal> applicableCharges;
-    private BigDecimal dueDateFeeIncome;
-    private BigDecimal dueDatePenaltyIncome;
-    private BigDecimal accruableIncome;
-
-    private BigDecimal creditedFee;
-    private BigDecimal creditedPenalty;
-
-    public LoanScheduleAccrualData(final Long loanId, final Long officeId, final Integer installmentNumber, final LocalDate accruedTill,
-            final PeriodFrequencyType repaymentFrequency, final Integer repayEvery, final LocalDate dueDate, final LocalDate fromDate,
-            final Long repaymentScheduleId, final Long loanProductId, final BigDecimal interestIncome, final BigDecimal feeIncome,
-            final BigDecimal penaltyIncome, final BigDecimal accruedInterestIncome, final BigDecimal accruedFeeIncome,
-            final BigDecimal accruedPenaltyIncome, final CurrencyData currencyData, final LocalDate interestCalculatedFrom,
-            final BigDecimal waivedInterestIncome, BigDecimal creditedFee, BigDecimal creditedPenalty) {
-        this.loanId = loanId;
-        this.installmentNumber = installmentNumber;
-        this.officeId = officeId;
-        this.accruedTill = accruedTill;
-        this.dueDate = dueDate;
-        this.fromDate = fromDate;
-        this.repaymentScheduleId = repaymentScheduleId;
-        this.loanProductId = loanProductId;
-        this.interestIncome = interestIncome;
-        this.feeIncome = feeIncome;
-        this.penaltyIncome = penaltyIncome;
-        this.accruedFeeIncome = accruedFeeIncome;
-        this.accruedInterestIncome = accruedInterestIncome;
-        this.accruedPenaltyIncome = accruedPenaltyIncome;
-        this.currencyData = currencyData;
-        this.repaymentFrequency = repaymentFrequency;
-        this.repayEvery = repayEvery;
-        this.interestCalculatedFrom = interestCalculatedFrom;
-        this.waivedInterestIncome = waivedInterestIncome;
-        this.creditedFee = creditedFee;
-        this.creditedPenalty = creditedPenalty;
-    }
-
-    public Long getLoanId() {
-        return this.loanId;
-    }
-
-    public Long getOfficeId() {
-        return this.officeId;
-    }
-
-    public LocalDate getDueDate() {
-        return this.dueDate;
-    }
-
-    public LocalDate getDueDateAsLocaldate() {
-        return this.dueDate;
-    }
-
-    public Long getRepaymentScheduleId() {
-        return this.repaymentScheduleId;
-    }
-
-    public Long getLoanProductId() {
-        return this.loanProductId;
-    }
-
-    public BigDecimal getInterestIncome() {
-        return this.interestIncome;
-    }
-
-    public BigDecimal getFeeIncome() {
-        return this.feeIncome;
-    }
-
-    public BigDecimal getPenaltyIncome() {
-        return this.penaltyIncome;
-    }
-
-    public BigDecimal getAccruedInterestIncome() {
-        return this.accruedInterestIncome;
-    }
-
-    public BigDecimal getAccruedFeeIncome() {
-        return this.accruedFeeIncome;
-    }
-
-    public BigDecimal getAccruedPenaltyIncome() {
-        return this.accruedPenaltyIncome;
-    }
-
-    public CurrencyData getCurrencyData() {
-        return this.currencyData;
-    }
-
-    public LocalDate getAccruedTill() {
-        return this.accruedTill;
-    }
-
-    public LocalDate getFromDateAsLocaldate() {
-        return this.fromDate;
-    }
-
-    public PeriodFrequencyType getRepaymentFrequency() {
-        return this.repaymentFrequency;
-    }
-
-    public Integer getRepayEvery() {
-        return this.repayEvery;
-    }
-
-    public LocalDate getInterestCalculatedFrom() {
-        return this.interestCalculatedFrom;
-    }
-
-    public Integer getInstallmentNumber() {
-        return this.installmentNumber;
-    }
-
-    public Map<LoanChargeData, BigDecimal> getApplicableCharges() {
-        return this.applicableCharges;
-    }
-
-    public BigDecimal getDueDateFeeIncome() {
-        return this.dueDateFeeIncome;
-    }
-
-    public BigDecimal getDueDatePenaltyIncome() {
-        return this.dueDatePenaltyIncome;
-    }
-
-    public void updateChargeDetails(final Map<LoanChargeData, BigDecimal> applicableCharges, final BigDecimal dueDateFeeIncome,
-            final BigDecimal dueDatePenaltyIncome) {
-        this.applicableCharges = applicableCharges;
-        this.dueDateFeeIncome = dueDateFeeIncome;
-        this.dueDatePenaltyIncome = dueDatePenaltyIncome;
-    }
-
-    public BigDecimal getWaivedInterestIncome() {
-        return this.waivedInterestIncome;
-    }
-
-    public BigDecimal getAccruableIncome() {
-        return this.accruableIncome;
-    }
-
-    public void updateAccruableIncome(BigDecimal accruableIncome) {
-        this.accruableIncome = accruableIncome;
-    }
-
-    public BigDecimal getCreditedFee() {
-        return this.creditedFee;
-    }
-
-    public BigDecimal getCreditedPenalty() {
-        return this.creditedPenalty;
-    }
-
-}
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 280693da5..27bb50d75 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -18,6 +18,8 @@
  */
 package org.apache.fineract.portfolio.loanaccount.domain;
 
+import static org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE;
+
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
@@ -740,6 +742,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
      * is lesser than todays date. If not, the transaction date is set to todays date
      */
     public LoanTransaction handleChargeAppliedTransaction(final LoanCharge loanCharge, final LocalDate suppliedTransactionDate) {
+        if (isProgressiveSchedule()) {
+            return null;
+        }
         final Money chargeAmount = loanCharge.getAmount(getCurrency());
         Money feeCharges = chargeAmount;
         Money penaltyCharges = Money.zero(getCurrency());
@@ -1053,7 +1058,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
             LoanCharge charge = loanCharge;
             // add new charges
             if (loanCharge.getId() == null) {
-                LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
+                LoanTrancheDisbursementCharge loanTrancheDisbursementCharge;
                 loanCharge.update(this);
                 if (this.loanProduct.isMultiDisburseLoan() && loanCharge.isTrancheDisbursementCharge()) {
                     loanCharge.getTrancheDisbursementCharge().getloanDisbursementDetails().updateLoan(this);
@@ -3563,4 +3568,8 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom<Long> {
     public LoanRepaymentScheduleTransactionProcessor getTransactionProcessor() {
         return transactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode);
     }
+
+    public boolean isProgressiveSchedule() {
+        return getLoanProductRelatedDetail().getLoanScheduleType() == PROGRESSIVE;
+    }
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java
index 31520704b..6f7f67750 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingService.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.portfolio.loanaccount.service;
 
+import jakarta.validation.constraints.NotNull;
 import java.time.LocalDate;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.springframework.transaction.annotation.Transactional;
@@ -25,13 +26,13 @@ import org.springframework.transaction.annotation.Transactional;
 public interface LoanAccrualActivityProcessingService {
 
     @Transactional
-    void makeAccrualActivityTransaction(Long loanId, LocalDate currentDate);
+    void makeAccrualActivityTransaction(@NotNull Long loanId, @NotNull LocalDate currentDate);
 
-    Loan makeAccrualActivityTransaction(Loan loan, LocalDate currentDate);
+    void makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LocalDate currentDate);
 
     @Transactional
-    void processAccrualActivityForLoanClosure(Loan loan);
+    void processAccrualActivityForLoanClosure(@NotNull Loan loan);
 
     @Transactional
-    void processAccrualActivityForLoanReopen(Loan loan);
+    void processAccrualActivityForLoanReopen(@NotNull Loan loan);
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java
index 0a0c84997..bf96b21a6 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualTransactionBusinessEventServiceImpl.java
@@ -20,7 +20,9 @@ package org.apache.fineract.portfolio.loanaccount.service;
 
 import java.util.List;
 import lombok.RequiredArgsConstructor;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualAdjustmentTransactionBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
@@ -33,8 +35,12 @@ public class LoanAccrualTransactionBusinessEventServiceImpl implements LoanAccru
     @Override
     public void raiseBusinessEventForAccrualTransactions(Loan loan, List<Long> existingTransactionIds) {
         for (final LoanTransaction transaction : loan.getLoanTransactions()) {
-            if (transaction.isNotReversed() && transaction.isAccrual() && !existingTransactionIds.contains(transaction.getId())) {
-                businessEventNotifierService.notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(transaction));
+            if (transaction.isNotReversed() && (transaction.isAccrual() || transaction.isAccrualAdjustment())
+                    && !existingTransactionIds.contains(transaction.getId())) {
+                LoanTransactionBusinessEvent businessEvent = transaction.isAccrual()
+                        ? new LoanAccrualTransactionCreatedBusinessEvent(transaction)
+                        : new LoanAccrualAdjustmentTransactionBusinessEvent(transaction);
+                businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
             }
         }
     }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java
index 91addc4f6..297774a04 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java
@@ -35,16 +35,14 @@ public interface LoanAccrualsProcessingService {
 
     void reprocessExistingAccruals(@NotNull Loan loan);
 
-    void processAccrualsForInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled);
+    void processAccrualsOnInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled, boolean addJournal);
 
-    void processIncomePostingAndAccruals(@NotNull Loan loan);
+    void addIncomePostingAndAccruals(Long loanId) throws Exception;
 
-    void addIncomeAndAccrualTransactions(Long loanId) throws Exception;
+    void processIncomePostingAndAccruals(@NotNull Loan loan);
 
     void processAccrualsOnLoanClosure(@NotNull Loan loan);
 
-    void processAccrualsOnLoanReopen(@NotNull Loan loan);
-
     void processAccrualsOnLoanForeClosure(@NotNull Loan loan, @NotNull LocalDate foreClosureDate,
             @NotNull List<LoanTransaction> newAccrualTransactions);
 }
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
index 97bd23535..f64f2d71a 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
@@ -30,7 +30,6 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
 import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
 import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.springframework.transaction.annotation.Transactional;
@@ -53,16 +52,6 @@ public interface LoanWritePlatformService {
     CommandProcessingResult makeLoanRepaymentWithChargeRefundChargeType(LoanTransactionType repaymentTransactionType, Long loanId,
             JsonCommand command, boolean isRecoveryRepayment, String chargeRefundChargeType);
 
-    @Transactional
-    Loan reverseReplayAccrualActivityTransaction(Loan loan, LoanTransaction loanTransaction, LoanRepaymentScheduleInstallment installment,
-            LocalDate transactionDate);
-
-    @Transactional
-    Loan makeAccrualActivityTransaction(Loan loan, LoanRepaymentScheduleInstallment installment, LocalDate transactionDate);
-
-    @Transactional
-    Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction accrualActivityTransaction);
-
     @Transactional
     CommandProcessingResult makeInterestPaymentWaiver(JsonCommand command);
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index 6452ced33..a1a25ee0b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -293,10 +293,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
             this.noteRepository.save(note);
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
 
         setLoanDelinquencyTag(loan, transactionDate);
 
@@ -306,6 +304,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
             businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
             businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
         }
+
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         // disable all active standing orders linked to this loan if status
         // changes to closed
         disableStandingInstructionsLinkedToClosedLoan(loan);
@@ -492,13 +494,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
             this.noteRepository.save(note);
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
         businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction));
+
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
         return newPaymentTransaction;
     }
 
@@ -726,13 +728,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
             this.noteRepository.save(note);
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
         businessEventNotifierService
                 .notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction));
 
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         return newCreditBalanceRefundTransaction;
     }
 
@@ -778,13 +782,14 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
             this.noteRepository.save(note);
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
         businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction));
 
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         builderResult.withEntityId(newRefundTransaction.getId()).withOfficeId(loan.getOfficeId()).withClientId(loan.getClientId())
                 .withGroupId(loan.getGroupId());
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java
index d7f18f226..68d3e5d79 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/addperiodicaccrualentriesforloanswithincomepostedastransactions/AddPeriodicAccrualEntriesForLoansTasklet.java
@@ -46,7 +46,7 @@ public class AddPeriodicAccrualEntriesForLoansTasklet implements Tasklet {
             List<Throwable> errors = new ArrayList<>();
             for (Long loanId : loanIds) {
                 try {
-                    loanAccrualsProcessingService.addIncomeAndAccrualTransactions(loanId);
+                    loanAccrualsProcessingService.addIncomePostingAndAccruals(loanId);
                 } catch (Exception e) {
                     log.error("Failed to add income and accrual transaction for loan {}", loanId, e);
                     errors.add(e);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
index 0d14d5841..e1df63373 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java
@@ -466,13 +466,13 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
                 replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
             }
             loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
-            // update the loan object
-            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-            loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
 
-            loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan, true);
+            loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
             businessEventNotifierService.notifyPostBusinessEvent(new LoanRescheduledDueAdjustScheduleBusinessEvent(loan));
 
+            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+            loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
             return new CommandProcessingResultBuilder().withCommandId(jsonCommand.commandId()).withEntityId(loanRescheduleRequestId)
                     .withLoanId(loanRescheduleRequest.getLoan().getId()).with(changes).withClientId(loan.getClientId())
                     .withOfficeId(loan.getOfficeId()).withGroupId(loan.getGroupId()).build();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java
index 36d067243..2f2373966 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java
@@ -18,24 +18,29 @@
  */
 package org.apache.fineract.portfolio.loanaccount.service;
 
+import jakarta.validation.constraints.NotNull;
 import java.math.BigDecimal;
 import java.time.LocalDate;
+import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Optional;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-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.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPostBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPreBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 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.LoanTransactionType;
-import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -46,131 +51,179 @@ import org.springframework.transaction.annotation.Transactional;
 public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActivityProcessingService {
 
     private final LoanRepositoryWrapper loanRepositoryWrapper;
-    private final LoanWritePlatformService loanWritePlatformService;
     private final ExternalIdFactory externalIdFactory;
     private final BusinessEventNotifierService businessEventNotifierService;
-    private final LoanChargeValidator loanChargeValidator;
+    private final LoanTransactionAssembler loanTransactionAssembler;
+    private final LoanAccountDomainService loanAccountDomainService;
 
     @Override
     @Transactional(propagation = Propagation.REQUIRES_NEW)
-    public void makeAccrualActivityTransaction(Long loanId, final LocalDate currentDate) {
+    public void makeAccrualActivityTransaction(@NotNull Long loanId, @NotNull LocalDate currentDate) {
         Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
         makeAccrualActivityTransaction(loan, currentDate);
     }
 
     @Override
-    public Loan makeAccrualActivityTransaction(Loan loan, final LocalDate currentDate) {
-        if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
-            // check if loan has installment due on business day
-            Optional<LoanRepaymentScheduleInstallment> first = loan.getRepaymentScheduleInstallments().stream()
-                    .filter(loanRepaymentScheduleInstallment -> loanRepaymentScheduleInstallment.getDueDate().isEqual(currentDate))
-                    .findFirst();
-            if (first.isPresent()) {
-                final LoanRepaymentScheduleInstallment installment = first.get();
-                // check if there is no not-replayed-accrual-activity related to business date
-                List<LoanTransaction> loanTransactions = loan.getLoanTransactions(loanTransaction -> loanTransaction.isNotReversed()
-                        && loanTransaction.isAccrualActivity() && loanTransaction.getTransactionDate().isEqual(currentDate));
-                if (loanTransactions.isEmpty()) {
-                    loan = loanWritePlatformService.makeAccrualActivityTransaction(loan, installment, currentDate);
-                }
+    public void makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LocalDate currentDate) {
+        if (!loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
+            return;
+        }
+        // check if loan has installment in the past or due on current date
+        List<LoanRepaymentScheduleInstallment> installments = loan
+                .getRepaymentScheduleInstallments(i -> !i.isDownPayment() && !DateUtils.isBefore(currentDate, i.getDueDate()));
+        for (LoanRepaymentScheduleInstallment installment : installments) {
+            LocalDate dueDate = installment.getDueDate();
+            // check if there is any not-replayed-accrual-activity related to business date
+            ArrayList<LoanTransaction> existingActivities = new ArrayList<>(
+                    loan.getLoanTransactions(t -> t.isNotReversed() && t.isAccrualActivity() && t.getTransactionDate().isEqual(dueDate)));
+            boolean hasExisting = !existingActivities.isEmpty();
+            LoanTransaction existingActivity = hasExisting ? existingActivities.get(0) : null;
+            makeOrReplayActivity(loan, installment, existingActivity);
+            if (hasExisting) {
+                existingActivities.remove(existingActivity);
+                existingActivities.forEach(this::reverseAccrualActivityTransaction);
             }
         }
-        return loan;
     }
 
     @Override
     @Transactional
-    public void processAccrualActivityForLoanClosure(Loan loan) {
-        LocalDate date = loan.isOverPaid() ? loan.getOverpaidOnDate() : loan.getClosedOnDate();
-        List<LoanTransaction> accrualActivityTransaction = loan.getLoanTransactions().stream().filter(LoanTransaction::isNotReversed)
-                .filter(LoanTransaction::isAccrualActivity).filter(loanTransaction -> loanTransaction.getDateOf().isAfter(date)).toList();
-        if (!accrualActivityTransaction.isEmpty()) {
-            accrualActivityTransaction.forEach(this::reverseAccrualActivityTransaction);
-        }
-        LoanTransaction loanTransaction = assembleClosingAccrualActivityTransaction(loan, date);
-        if (loanTransaction.getAmount().compareTo(BigDecimal.ZERO) != 0) {
-            loanWritePlatformService.makeAccrualActivityTransaction(loan, loanTransaction);
+    public void processAccrualActivityForLoanClosure(@NotNull Loan loan) {
+        if (!loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
+            return;
         }
-    }
-
-    private void reverseAccrualActivityTransaction(LoanTransaction loanTransaction) {
-        loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction, "reversed");
-        loanTransaction.reverse();
-        LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
-        businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
-    }
+        LocalDate date = loan.isOverPaid() ? loan.getOverpaidOnDate() : loan.getClosedOnDate();
+        // reverse after closure activities
+        loan.getLoanTransactions(t -> t.isAccrualActivity() && !t.isReversed() && t.getDateOf().isAfter(date))
+                .forEach(this::reverseAccrualActivityTransaction);
 
-    private LoanTransaction assembleClosingAccrualActivityTransaction(Loan loan, LocalDate date) {
-        // collect fees
+        // calculate activity amounts
         BigDecimal feeChargesPortion = BigDecimal.ZERO;
-        // collect penalties
         BigDecimal penaltyChargesPortion = BigDecimal.ZERO;
-        // collect interests
         BigDecimal interestPortion = BigDecimal.ZERO;
-        var currency = loan.getCurrency();
-        // sum up all accruals
+        // collect installment amounts
         for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
-            feeChargesPortion = installment.getFeeAccrued(currency).getAmount().add(feeChargesPortion);
-            penaltyChargesPortion = installment.getPenaltyAccrued(currency).getAmount().add(penaltyChargesPortion);
-            interestPortion = installment.getInterestAccrued(currency).getAmount().add(interestPortion);
+            feeChargesPortion = MathUtil.add(feeChargesPortion, installment.getFeeChargesCharged());
+            penaltyChargesPortion = MathUtil.add(penaltyChargesPortion, installment.getPenaltyCharges());
+            interestPortion = MathUtil.add(interestPortion, installment.getInterestCharged());
         }
         List<LoanTransaction> accrualActivities = loan.getLoanTransactions().stream().filter(LoanTransaction::isAccrualActivity)
                 .filter(LoanTransaction::isNotReversed).toList();
-        // subtract already Posted accruals
+        // subtract already posted activities
         for (LoanTransaction accrualActivity : accrualActivities) {
-            if (accrualActivity.getFeeChargesPortion() != null) {
-                feeChargesPortion = feeChargesPortion.subtract(accrualActivity.getFeeChargesPortion());
-            }
-            if (accrualActivity.getPenaltyChargesPortion() != null) {
-                penaltyChargesPortion = penaltyChargesPortion.subtract(accrualActivity.getPenaltyChargesPortion());
-            }
-            if (accrualActivity.getInterestPortion() != null) {
-                interestPortion = interestPortion.subtract(accrualActivity.getInterestPortion());
-            }
+            feeChargesPortion = MathUtil.subtract(feeChargesPortion, accrualActivity.getFeeChargesPortion());
+            penaltyChargesPortion = MathUtil.subtract(penaltyChargesPortion, accrualActivity.getPenaltyChargesPortion());
+            interestPortion = MathUtil.subtract(interestPortion, accrualActivity.getInterestPortion());
         }
-
-        BigDecimal transactionAmount = feeChargesPortion.add(penaltyChargesPortion).add(interestPortion);
-        ExternalId externalId = externalIdFactory.create();
-
-        return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), date, transactionAmount, null,
-                interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
+        BigDecimal transactionAmount = MathUtil.add(feeChargesPortion, penaltyChargesPortion, interestPortion);
+        if (!MathUtil.isGreaterThanZero(transactionAmount)) {
+            return;
+        }
+        if (MathUtil.isLessThanZero(feeChargesPortion) || MathUtil.isLessThanZero(penaltyChargesPortion)
+                || MathUtil.isLessThanZero(interestPortion)) {
+            // TODO reverse latest accrual activity if any amount is negative
+            return;
+        }
+        LoanTransaction newActivity = new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), date,
+                transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null,
+                externalIdFactory.create());
+        makeAccrualActivityTransaction(loan, newActivity);
     }
 
     @Override
     @Transactional
-    public void processAccrualActivityForLoanReopen(Loan loan) {
-        LoanTransaction lastAccrualActivityMarkedToReverse = null;
-        List<LoanTransaction> accrualActivityTransaction = loan.getLoanTransactions().stream()
-                .filter(loanTransaction -> loanTransaction.isNotReversed() && loanTransaction.isAccrualActivity())
-                .sorted(Comparator.comparing(LoanTransaction::getDateOf)).toList();
+    public void processAccrualActivityForLoanReopen(@NotNull Loan loan) {
+        if (!loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
+            return;
+        }
         // grab the latest AccrualActivityTransaction
         // it does not matter if it is on an installment due date or not because it was posted due to loan close
-        if (!accrualActivityTransaction.isEmpty()) {
-            lastAccrualActivityMarkedToReverse = accrualActivityTransaction.get(accrualActivityTransaction.size() - 1);
-        }
+        LoanTransaction lastAccrualActivityMarkedToReverse = loan.getLoanTransactions().stream()
+                .filter(loanTransaction -> loanTransaction.isNotReversed() && loanTransaction.isAccrualActivity())
+                .sorted(Comparator.comparing(LoanTransaction::getDateOf)).reduce((first, second) -> second).orElse(null);
         final LocalDate lastAccrualActivityTransactionDate = lastAccrualActivityMarkedToReverse == null ? null
                 : lastAccrualActivityMarkedToReverse.getDateOf();
         LocalDate today = DateUtils.getBusinessLocalDate();
         final List<LoanRepaymentScheduleInstallment> installmentsBetweenBusinessDateAndLastAccrualActivityTransactionDate = loan
                 .getRepaymentScheduleInstallments().stream()
-                .filter(installment -> installment.getDueDate().isBefore(today) && (lastAccrualActivityTransactionDate == null
-                        || installment.getDueDate().isAfter(lastAccrualActivityTransactionDate)
-                        // if close event happened on installment due date
-                        // we should reverse replay it to calculate installment related accrual parts only
-                        || installment.getDueDate().isEqual(lastAccrualActivityTransactionDate)))
+                .filter(installment -> installment.getDueDate().isBefore(today)
+                        && (DateUtils.isAfter(installment.getDueDate(), lastAccrualActivityTransactionDate)
+                                // if close event happened on installment due date
+                                // we should reverse replay it to calculate installment related accrual parts only
+                                || installment.getDueDate().isEqual(lastAccrualActivityTransactionDate)))
                 .sorted(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)).toList();
         for (LoanRepaymentScheduleInstallment installment : installmentsBetweenBusinessDateAndLastAccrualActivityTransactionDate) {
-            if (lastAccrualActivityMarkedToReverse != null) {
-                loanWritePlatformService.reverseReplayAccrualActivityTransaction(loan, lastAccrualActivityMarkedToReverse, installment,
-                        installment.getDueDate());
-                lastAccrualActivityMarkedToReverse = null;
-            } else {
-                loanWritePlatformService.makeAccrualActivityTransaction(loan, installment, installment.getDueDate());
-            }
+            makeOrReplayActivity(loan, installment, lastAccrualActivityMarkedToReverse);
+            lastAccrualActivityMarkedToReverse = null;
         }
         if (lastAccrualActivityMarkedToReverse != null) {
             reverseAccrualActivityTransaction(lastAccrualActivityMarkedToReverse);
         }
     }
 
+    private void makeOrReplayActivity(@NotNull Loan loan, @NotNull LoanRepaymentScheduleInstallment installment,
+            LoanTransaction existingActivity) {
+        LocalDate dueDate = installment.getDueDate();
+        if (existingActivity == null) {
+            makeAccrualActivityTransaction(loan, installment, dueDate);
+        } else {
+            reverseReplayAccrualActivityTransaction(loan, existingActivity, installment, dueDate);
+        }
+    }
+
+    private LoanTransaction reverseReplayAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanTransaction loanTransaction,
+            @NotNull LoanRepaymentScheduleInstallment installment, @NotNull LocalDate transactionDate) {
+        if (validateActivityTransaction(installment, loanTransaction)) {
+            return loanTransaction;
+        }
+
+        LoanTransaction newLoanTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
+                transactionDate);
+        if (newLoanTransaction != null) {
+            newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
+            newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction,
+                    loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
+            loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
+            loan.addLoanTransaction(newLoanTransaction);
+
+            LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
+            data.setNewTransactionDetail(newLoanTransaction);
+            businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+        }
+        reverseAccrualActivityTransaction(loanTransaction);
+        return newLoanTransaction;
+    }
+
+    private boolean validateActivityTransaction(@NotNull LoanRepaymentScheduleInstallment installment,
+            @NotNull LoanTransaction transaction) {
+        return DateUtils.isEqual(installment.getDueDate(), transaction.getDateOf())
+                && MathUtil.isEqualTo(transaction.getInterestPortion(), installment.getInterestCharged())
+                && MathUtil.isEqualTo(transaction.getFeeChargesPortion(), installment.getFeeChargesCharged())
+                && MathUtil.isEqualTo(transaction.getPenaltyChargesPortion(), installment.getPenaltyCharges());
+    }
+
+    private void reverseAccrualActivityTransaction(LoanTransaction loanTransaction) {
+        loanTransaction.reverse();
+        loanTransaction.updateExternalId(null);
+        LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
+        businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+    }
+
+    private LoanTransaction makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanRepaymentScheduleInstallment installment,
+            @NotNull LocalDate transactionDate) {
+        LoanTransaction newAccrualActivityTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
+                transactionDate);
+        return newAccrualActivityTransaction == null ? null : makeAccrualActivityTransaction(loan, newAccrualActivityTransaction);
+    }
+
+    private LoanTransaction makeAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanTransaction newAccrualActivityTransaction) {
+        businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
+        newAccrualActivityTransaction = loanAccountDomainService
+                .saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
+
+        loan.addLoanTransaction(newAccrualActivityTransaction);
+        businessEventNotifierService
+                .notifyPostBusinessEvent(new LoanTransactionAccrualActivityPostBusinessEvent(newAccrualActivityTransaction));
+        return newAccrualActivityTransaction;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java
similarity index 52%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java
index 06f402c41..9697a56d0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java
@@ -22,14 +22,15 @@ import jakarta.annotation.PostConstruct;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.fineract.infrastructure.event.business.BusinessEventListener;
-import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 
 @Slf4j
 @RequiredArgsConstructor
-public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlatformService {
+public class LoanAccrualEventService {
 
     private final BusinessEventNotifierService businessEventNotifierService;
     private final LoanAccrualsProcessingService loanAccrualsProcessingService;
@@ -37,44 +38,34 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
 
     @PostConstruct
     public void addListeners() {
-        businessEventNotifierService.addPostBusinessEventListener(LoanStatusChangedBusinessEvent.class, new LoanStatusChangedListener());
-        businessEventNotifierService.addPostBusinessEventListener(LoanStatusChangedBusinessEvent.class,
-                new LoanAccrualActivityPostingLoanStatusChangedListener());
+        businessEventNotifierService.addPostBusinessEventListener(LoanCloseBusinessEvent.class, new LoanCloseListener());
+        businessEventNotifierService.addPostBusinessEventListener(LoanBalanceChangedBusinessEvent.class, new LoanBalanceChangedListener());
     }
 
-    private final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
+    private final class LoanCloseListener implements BusinessEventListener<LoanCloseBusinessEvent> {
 
         @Override
-        public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
+        public void onBusinessEvent(LoanCloseBusinessEvent event) {
             final Loan loan = event.get();
-            log.debug("Loan Status change for loan {}", loan.getId());
-            LoanStatus oldStatus = event.getOldStatus();
-            LoanStatus newStatus = loan.getStatus();
-            if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
-                log.debug("Loan Status {} for loan {}", newStatus.getCode(), loan.getId());
-                loan.updateLoanSummaryDerivedFields();
+            LoanStatus status = loan.getStatus();
+            if (status.isClosedObligationsMet() || status.isOverpaid()) {
+                log.debug("Loan closure on accrual for loan {}", loan.getId());
                 loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
-            } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
-                loan.handleMaturityDateActivate();
-                loanAccrualsProcessingService.processAccrualsOnLoanReopen(loan);
+                loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
             }
         }
     }
 
-    private final class LoanAccrualActivityPostingLoanStatusChangedListener
-            implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
+    private final class LoanBalanceChangedListener implements BusinessEventListener<LoanBalanceChangedBusinessEvent> {
 
         @Override
-        public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
+        public void onBusinessEvent(LoanBalanceChangedBusinessEvent event) {
             final Loan loan = event.get();
-            if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
-                LoanStatus oldStatus = event.getOldStatus();
-                LoanStatus newStatus = loan.getStatus();
-                if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
-                    loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
-                } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
-                    loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan);
-                }
+            LoanStatus status = loan.getStatus();
+            if (status.isClosedObligationsMet() || status.isOverpaid()) {
+                log.debug("Loan balance change on accrual for loan {}", loan.getId());
+                loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
+                loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
             }
         }
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
index bb867e299..55d5de5b7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java
@@ -56,14 +56,12 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
 import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
 import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
 import org.apache.fineract.organisation.monetary.domain.Money;
-import org.apache.fineract.organisation.office.domain.OfficeRepository;
 import org.apache.fineract.portfolio.loanaccount.data.AccrualChargeData;
 import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodData;
 import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodsData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy;
-import org.apache.fineract.portfolio.loanaccount.domain.LoanChargeRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalcualtionAdditionalDetails;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanInterestRecalculationDetails;
@@ -77,8 +75,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepayme
 import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
-import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
-import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator;
 import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import org.springframework.stereotype.Component;
@@ -102,9 +98,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
     private final LoanTransactionRepository loanTransactionRepository;
     private final LoanScheduleGeneratorFactory loanScheduleFactory;
     private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
-    private final OfficeRepository officeRepository;
-    private final LoanChargeRepository loanChargeRepository;
-    private final LoanChargeValidator loanChargeValidator;
 
     /**
      * method adds accrual for batch job "Add Periodic Accrual Transactions" and add accruals api for Loan
@@ -167,9 +160,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
      */
     @Override
     public void reprocessExistingAccruals(@NotNull Loan loan) {
-        if (isProgressiveAccrual(loan)) {
-            return;
-        }
         List<LoanTransaction> accrualTransactions = retrieveListOfAccrualTransactions(loan);
         if (!accrualTransactions.isEmpty()) {
             if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
@@ -185,7 +175,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
      */
     @Override
     @Transactional
-    public void processAccrualsForInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled) {
+    public void processAccrualsOnInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled, boolean addJournal) {
         if (isProgressiveAccrual(loan)) {
             return;
         }
@@ -194,13 +184,31 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
             return;
         }
         try {
-            addPeriodicAccruals(accruedTill, loan);
+            addAccruals(loan, accruedTill, true, false, addJournal);
         } catch (Exception e) {
             String globalisationMessageCode = "error.msg.accrual.exception";
             throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage(), e);
         }
     }
 
+    @Transactional
+    @Override
+    public void addIncomePostingAndAccruals(Long loanId) throws LoanNotFoundException {
+        if (loanId == null) {
+            return;
+        }
+        Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
+        if (isProgressiveAccrual(loan)) {
+            return;
+        }
+        final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
+        final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
+        processIncomePostingAndAccruals(loan);
+        this.loanRepositoryWrapper.saveAndFlush(loan);
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+    }
+
     /**
      * method calculates accruals for loan with interest recalculation and compounding to be posted as income
      */
@@ -228,26 +236,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
         }
         List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
         LoanRepaymentScheduleInstallment lastInstallment = LoanRepaymentScheduleInstallment.getLastNonDownPaymentInstallment(installments);
-        reverseTransactionsAfter(incomeTransactions, lastInstallment.getDueDate());
-        reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate());
-    }
-
-    @Transactional
-    @Override
-    public void addIncomeAndAccrualTransactions(Long loanId) throws LoanNotFoundException {
-        if (loanId == null) {
-            return;
-        }
-        Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
-        if (isProgressiveAccrual(loan)) {
-            return;
-        }
-        final List<Long> existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds());
-        final List<Long> existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds());
-        processIncomePostingAndAccruals(loan);
-        this.loanRepositoryWrapper.saveAndFlush(loan);
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+        reverseTransactionsAfter(incomeTransactions, lastInstallment.getDueDate(), false);
+        reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate(), false);
     }
 
     /**
@@ -264,32 +254,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
         processIncomeAndAccrualTransactionOnLoanClosure(loan);
     }
 
-    @Override
-    public void processAccrualsOnLoanReopen(@NotNull Loan loan) {
-        if (isProgressiveAccrual(loan)) {
-            List<LoanTransaction> accruals = retrieveListOfAccrualTransactions(loan);
-            LocalDate accruedTill = loan.getAccruedTill();
-            // if accruedTill is null, the all the accrual transactions will be reversed here
-            if (reverseTransactionsAfter(accruals, accruedTill)) {
-                return;
-            }
-            LoanTransaction closureAccrual = accruals.stream()
-                    .filter(t -> !t.isReversed() && t.isAccrual() && DateUtils.isEqual(t.getTransactionDate(), accruedTill))
-                    .sorted(LoanTransactionComparator.INSTANCE).reduce((first, second) -> second).orElse(null);
-            if (closureAccrual == null) {
-                return;
-            }
-            int firstInstallmentNumber = fetchFirstNormalInstallmentNumber(loan.getRepaymentScheduleInstallments());
-            LoanRepaymentScheduleInstallment installment = loan.getRepaymentScheduleInstallment(
-                    i -> isInPeriod(closureAccrual.getDateOf(), i, i.getInstallmentNumber().equals(firstInstallmentNumber)));
-            if (MathUtil.isGreaterThan(closureAccrual.getInterestPortion(), installment.getInterestCharged())
-                    || MathUtil.isGreaterThan(closureAccrual.getFeeChargesPortion(), installment.getFeeChargesCharged())
-                    || MathUtil.isGreaterThan(closureAccrual.getPenaltyChargesPortion(), installment.getPenaltyCharges())) {
-                reverseAccrual(closureAccrual);
-            }
-        }
-    }
-
     /**
      * method calculates accruals for loan on loan fore closure
      */
@@ -301,7 +265,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
                 && (loan.getAccruedTill() == null || !DateUtils.isEqual(foreClosureDate, loan.getAccruedTill()))) {
             final LoanRepaymentScheduleInstallment foreCloseDetail = loan.fetchLoanForeclosureDetail(foreClosureDate);
             MonetaryCurrency currency = loan.getCurrency();
-            reverseTransactionsAfter(retrieveListOfAccrualTransactions(loan), foreClosureDate);
+            reverseTransactionsAfter(retrieveListOfAccrualTransactions(loan), foreClosureDate, false);
 
             HashMap<String, Object> incomeDetails = new HashMap<>();
 
@@ -331,20 +295,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
             return;
         }
         List<LoanTransaction> existingAccruals = retrieveListOfAccrualTransactions(loan);
-        reverseTransactionsAfter(existingAccruals, loan.getLastLoanRepaymentScheduleInstallment().getDueDate());
+        LocalDate lastDueDate = loan.getLastLoanRepaymentScheduleInstallment().getDueDate();
+        reverseTransactionsAfter(existingAccruals, lastDueDate, addJournal);
         ensureAccrualTransactionMappings(loan);
+        if (DateUtils.isAfter(tillDate, lastDueDate)) {
+            tillDate = lastDueDate;
+        }
 
         boolean progressiveAccrual = isProgressiveAccrual(loan);
         LocalDate accruedTill = loan.getAccruedTill();
-        if (progressiveAccrual && accruedTill != null && !DateUtils.isAfter(tillDate, accruedTill)
-                && existingAccruals.stream().anyMatch(t -> !t.isReversed() && !DateUtils.isBefore(t.getDateOf(), tillDate))) {
-            return;
+        LocalDate businessDate = DateUtils.getBusinessLocalDate();
+        LocalDate accrualDate = isFinal
+                ? (progressiveAccrual ? (DateUtils.isBefore(lastDueDate, businessDate) ? lastDueDate : businessDate)
+                        : getFinalAccrualTransactionDate(loan))
+                : tillDate;
+        if (progressiveAccrual && accruedTill != null && !DateUtils.isAfter(tillDate, accruedTill)) {
+            if (isFinal) {
+                reverseTransactionsAfter(existingAccruals, accrualDate, addJournal);
+            } else if (existingAccruals.stream().anyMatch(t -> !t.isReversed() && !DateUtils.isBefore(t.getDateOf(), accrualDate))) {
+                return;
+            }
         }
 
         AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, tillDate, periodic);
-
-        LocalDate accrualDate = isFinal ? (progressiveAccrual ? DateUtils.getBusinessLocalDate() : getFinalAccrualTransactionDate(loan))
-                : tillDate;
         boolean mergeTransactions = isFinal || progressiveAccrual;
         MonetaryCurrency currency = loan.getLoanProductRelatedDetail().getCurrency();
         List<LoanTransaction> accrualTransactions = new ArrayList<>();
@@ -399,17 +372,17 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
             return;
         }
 
-        if (!isFinal) {
-            loan.setAccruedTill(tillDate);
+        if (!isFinal || progressiveAccrual) {
+            loan.setAccruedTill(isFinal ? accrualDate : tillDate);
         }
         ArrayList<Map<String, Object>> newTransactionMapping = new ArrayList<>();
         for (LoanTransaction accrualTransaction : accrualTransactions) {
             accrualTransaction = loanTransactionRepository.saveAndFlush(accrualTransaction);
-            LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
-                    ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
-                    : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
-            businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
             if (addJournal) {
+                LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual()
+                        ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction)
+                        : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction);
+                businessEventNotifierService.notifyPostBusinessEvent(businessEvent);
                 newTransactionMapping.add(accrualTransaction.toMapData(currency.getCode()));
             }
         }
@@ -584,7 +557,9 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
 
         Money unrecognizedWaived = MathUtil.toMoney(calcChargeUnrecognizedWaivedAmount(paidBys, tillDate), currency);
         Money transactionWaived = MathUtil.minusToZero(waived, unrecognizedWaived);
-        chargeData.setChargeAccrued(MathUtil.minusToZero(MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency), transactionWaived));
+        Money transactionAccrued = MathUtil.toMoney(calcChargeAccruedAmount(paidBys), currency);
+        chargeData.setTransactionAccrued(transactionAccrued);
+        chargeData.setChargeAccrued(MathUtil.minusToZero(transactionAccrued, transactionWaived));
 
         duePeriod.addCharge(chargeData);
     }
@@ -720,17 +695,29 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
             return;
         }
         ensureAccrualTransactionMappings(loan);
-        List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
-        boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate();
-        for (LoanRepaymentScheduleInstallment installment : installments) {
-            checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment);
+        LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
+        LocalDate lastDueDate = lastInstallment.getDueDate();
+        if (isProgressiveAccrual(loan)) {
+            AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, lastDueDate, true);
+            for (AccrualPeriodData period : accrualPeriods.getPeriods()) {
+                Money interestAccrued = period.getTransactionAccrued();
+                Money feeAccrued = period.getFeeTransactionAccrued();
+                Money penaltyAccrued = period.getPenaltyTransactionAccrued();
+                LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber());
+                installment.updateAccrualPortion(interestAccrued, feeAccrued, penaltyAccrued);
+            }
+        } else {
+            List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
+            boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate();
+            for (LoanRepaymentScheduleInstallment installment : installments) {
+                checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment);
+            }
         }
         // reverse accruals after last installment
-        LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
-        reverseTransactionsAfter(accrualTransactions, lastInstallment.getDueDate());
+        reverseTransactionsAfter(accrualTransactions, lastDueDate, false);
     }
 
-    private void reprocessNonPeriodicAccruals(Loan loan, final List<LoanTransaction> accruals) {
+    private void reprocessNonPeriodicAccruals(Loan loan, final List<LoanTransaction> accrualTransactions) {
         if (isProgressiveAccrual(loan)) {
             return;
         }
@@ -738,29 +725,25 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
         ExternalId externalId = ExternalId.empty();
         boolean isExternalIdAutoGenerationEnabled = configurationDomainService.isExternalIdAutoGenerationEnabled();
 
-        for (LoanTransaction loanTransaction : accruals) {
-            if (loanTransaction.getInterestPortion(loan.getCurrency()).isGreaterThanZero()) {
-                if (loanTransaction.getInterestPortion(loan.getCurrency()).isNotEqualTo(interestApplied)) {
-                    loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
-                            "reversed");
-                    loanTransaction.reverse();
+        for (LoanTransaction accrualTransaction : accrualTransactions) {
+            if (accrualTransaction.getInterestPortion(loan.getCurrency()).isGreaterThanZero()) {
+                if (accrualTransaction.getInterestPortion(loan.getCurrency()).isNotEqualTo(interestApplied)) {
+                    accrualTransaction.reverse();
                     if (isExternalIdAutoGenerationEnabled) {
                         externalId = ExternalId.generate();
                     }
-                    final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan,
+                    final LoanTransaction interestAccrualTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan,
                             interestApplied, loan.getDisbursementDate(), externalId);
-                    loan.addLoanTransaction(interestAppliedTransaction);
+                    loan.addLoanTransaction(interestAccrualTransaction);
                 }
             } else {
-                Set<LoanChargePaidBy> chargePaidBies = loanTransaction.getLoanChargesPaid();
+                Set<LoanChargePaidBy> chargePaidBies = accrualTransaction.getLoanChargesPaid();
                 for (final LoanChargePaidBy chargePaidBy : chargePaidBies) {
                     LoanCharge loanCharge = chargePaidBy.getLoanCharge();
                     Money chargeAmount = loanCharge.getAmount(loan.getCurrency());
-                    if (chargeAmount.isNotEqualTo(loanTransaction.getAmount(loan.getCurrency()))) {
-                        loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(),
-                                loanTransaction, "reversed");
-                        loanTransaction.reverse();
-                        loan.handleChargeAppliedTransaction(loanCharge, loanTransaction.getTransactionDate());
+                    if (chargeAmount.isNotEqualTo(accrualTransaction.getAmount(loan.getCurrency()))) {
+                        accrualTransaction.reverse();
+                        loan.handleChargeAppliedTransaction(loanCharge, accrualTransaction.getTransactionDate());
                     }
                 }
             }
@@ -786,8 +769,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
                     interest = interest.minus(accrualTransaction.getInterestPortion(currency));
                     fee = fee.minus(accrualTransaction.getFeeChargesPortion(currency));
                     penalty = penalty.minus(accrualTransaction.getPenaltyChargesPortion(currency));
-                    loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(accrualTransaction.getLoan(),
-                            accrualTransaction, "reversed");
                     accrualTransaction.reverse();
                 }
             }
@@ -869,8 +850,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
                     compoundingDetail.getAmount(), interest, fee, penalties, externalId);
             loan.addLoanTransaction(transaction);
         } else if (existingIncomeTransaction.getAmount(loan.getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
-            loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(existingIncomeTransaction.getLoan(),
-                    existingIncomeTransaction, "reversed");
             existingIncomeTransaction.reverse();
             LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
                     compoundingDetail.getAmount(), interest, fee, penalties, externalId);
@@ -889,8 +868,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
             if (existingAccrualTransaction == null
                     || !MathUtil.isEqualTo(existingAccrualTransaction.getAmount(), compoundingDetail.getAmount())) {
                 if (existingAccrualTransaction != null) {
-                    loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(existingAccrualTransaction.getLoan(),
-                            existingAccrualTransaction, "reversed");
                     existingAccrualTransaction.reverse();
                 }
                 LoanTransaction accrual = LoanTransaction.accrueTransaction(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(),
@@ -910,8 +887,8 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
                 && loan.getStatus().isClosedObligationsMet() && !loan.isNpa() && !loan.isChargedOff()) {
 
             LocalDate closedDate = loan.getClosedOnDate();
-            reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate);
-            reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate);
+            reverseTransactionsOnOrAfter(retrieveListOfIncomePostingTransactions(loan), closedDate, false);
+            reverseTransactionsOnOrAfter(retrieveListOfAccrualTransactions(loan), closedDate, false);
 
             HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
             determineCumulativeIncomeFromInstallments(loan, cumulativeIncomeFromInstallments);
@@ -1188,36 +1165,34 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
         }
     }
 
-    private boolean reverseTransactionsAfter(List<LoanTransaction> transactions, LocalDate effectiveDate) {
+    private boolean reverseTransactionsAfter(List<LoanTransaction> accrualTransactions, LocalDate effectiveDate, boolean addEvent) {
         boolean reversed = false;
-        for (LoanTransaction loanTransaction : transactions) {
-            if (DateUtils.isAfter(loanTransaction.getTransactionDate(), effectiveDate)) {
-                loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
-                        "reversed");
-                reverseAccrual(loanTransaction);
+        for (LoanTransaction accrualTransaction : accrualTransactions) {
+            if (!accrualTransaction.isReversed() && DateUtils.isAfter(accrualTransaction.getTransactionDate(), effectiveDate)) {
+                reverseAccrual(accrualTransaction, addEvent);
                 reversed = true;
             }
         }
         return reversed;
     }
 
-    private boolean reverseTransactionsOnOrAfter(List<LoanTransaction> transactions, LocalDate date) {
+    private boolean reverseTransactionsOnOrAfter(List<LoanTransaction> accrualTransactions, LocalDate date, boolean addEvent) {
         boolean reversed = false;
-        for (LoanTransaction loanTransaction : transactions) {
-            if (!DateUtils.isBefore(loanTransaction.getTransactionDate(), date)) {
-                loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
-                        "reversed");
-                reverseAccrual(loanTransaction);
+        for (LoanTransaction accrualTransaction : accrualTransactions) {
+            if (!accrualTransaction.isReversed() && !DateUtils.isBefore(accrualTransaction.getTransactionDate(), date)) {
+                reverseAccrual(accrualTransaction, addEvent);
                 reversed = true;
             }
         }
         return reversed;
     }
 
-    private void reverseAccrual(LoanTransaction transaction) {
+    private void reverseAccrual(LoanTransaction transaction, boolean addEvent) {
         transaction.reverse();
-        LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction);
-        businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+        if (addEvent) {
+            LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction);
+            businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
+        }
     }
 
     private LocalDate getFinalAccrualTransactionDate(Loan loan) {
@@ -1229,7 +1204,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
     }
 
     public boolean isProgressiveAccrual(@NotNull Loan loan) {
-        return loan.getLoanProductRelatedDetail().getLoanScheduleType() == LoanScheduleType.PROGRESSIVE;
+        return loan.isProgressiveSchedule();
     }
 
     private void setSetHelpers(Loan loan) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
index 3e3b8dee0..c8525e4de 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java
@@ -301,17 +301,18 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
             loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-
         if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && isAppliedOnBackDate
                 && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
-            loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                    loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+            loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
         }
         this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
 
         businessEventNotifierService.notifyPostBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge));
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
                 .withEntityId(loanCharge.getId()) //
                 .withEntityExternalId(loanCharge.getExternalId()) //
@@ -825,14 +826,14 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
                 loan = loanAccountDomainService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
             }
 
-            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-
             if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled() && runInterestRecalculation
                     && loan.isFeeCompoundingEnabledForInterestRecalculation()) {
-                loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                        loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+                loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false);
             }
             this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
+            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+            loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
         }
     }
 
@@ -1050,9 +1051,11 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
                 || (configurationDomainService.isImmediateChargeAccrualPostMaturityEnabled()
                         && DateUtils.getBusinessLocalDate().isAfter(loan.getMaturityDate()))) {
             final LoanTransaction applyLoanChargeTransaction = loan.handleChargeAppliedTransaction(loanCharge, null);
-            this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
-            businessEventNotifierService
-                    .notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(applyLoanChargeTransaction));
+            if (applyLoanChargeTransaction != null) {
+                this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction);
+                businessEventNotifierService
+                        .notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(applyLoanChargeTransaction));
+            }
         }
         return DateUtils.isBeforeBusinessDate(loanCharge.getDueLocalDate());
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
index 06f402c41..7d4e5a1a7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java
@@ -32,7 +32,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlatformService {
 
     private final BusinessEventNotifierService businessEventNotifierService;
-    private final LoanAccrualsProcessingService loanAccrualsProcessingService;
     private final LoanAccrualActivityProcessingService loanAccrualActivityProcessingService;
 
     @PostConstruct
@@ -42,21 +41,14 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
                 new LoanAccrualActivityPostingLoanStatusChangedListener());
     }
 
-    private final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
+    private static final class LoanStatusChangedListener implements BusinessEventListener<LoanStatusChangedBusinessEvent> {
 
         @Override
         public void onBusinessEvent(LoanStatusChangedBusinessEvent event) {
             final Loan loan = event.get();
             log.debug("Loan Status change for loan {}", loan.getId());
-            LoanStatus oldStatus = event.getOldStatus();
-            LoanStatus newStatus = loan.getStatus();
-            if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
-                log.debug("Loan Status {} for loan {}", newStatus.getCode(), loan.getId());
-                loan.updateLoanSummaryDerivedFields();
-                loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan);
-            } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
+            if (loan.isOpen()) {
                 loan.handleMaturityDateActivate();
-                loanAccrualsProcessingService.processAccrualsOnLoanReopen(loan);
             }
         }
     }
@@ -70,9 +62,7 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat
             if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) {
                 LoanStatus oldStatus = event.getOldStatus();
                 LoanStatus newStatus = loan.getStatus();
-                if (oldStatus.isActive() && (newStatus.isClosedObligationsMet() || newStatus.isOverpaid())) {
-                    loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan);
-                } else if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
+                if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) {
                     loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan);
                 }
             }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java
index 0e641a7df..11f60232a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanTransactionAssembler.java
@@ -18,6 +18,7 @@
  */
 package org.apache.fineract.portfolio.loanaccount.service;
 
+import jakarta.validation.constraints.NotNull;
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.Map;
@@ -26,7 +27,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.domain.ExternalId;
 import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
-import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
+import org.apache.fineract.infrastructure.core.service.MathUtil;
 import org.apache.fineract.organisation.monetary.domain.Money;
 import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -68,16 +69,17 @@ public class LoanTransactionAssembler {
                 txnExternalId, null);
     }
 
-    public LoanTransaction assembleAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
-            final LocalDate transactionDate) {
-        MonetaryCurrency currency = loan.getCurrency();
+    public LoanTransaction assembleAccrualActivityTransaction(@NotNull Loan loan, @NotNull LoanRepaymentScheduleInstallment installment,
+            @NotNull LocalDate transactionDate) {
         ExternalId externalId = externalIdFactory.create();
 
-        BigDecimal interestPortion = installment.getInterestCharged(currency).getAmount();
-        BigDecimal feeChargesPortion = installment.getFeeChargesCharged(currency).getAmount();
-        BigDecimal penaltyChargesPortion = installment.getPenaltyChargesCharged(currency).getAmount();
-        BigDecimal transactionAmount = interestPortion.add(feeChargesPortion).add(penaltyChargesPortion);
-        return new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), transactionDate,
-                transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId);
+        BigDecimal interestPortion = installment.getInterestCharged();
+        BigDecimal feeChargesPortion = installment.getFeeChargesCharged();
+        BigDecimal penaltyChargesPortion = installment.getPenaltyCharges();
+        BigDecimal transactionAmount = MathUtil.add(interestPortion, feeChargesPortion, penaltyChargesPortion);
+        return MathUtil.isGreaterThanZero(transactionAmount)
+                ? new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.ACCRUAL_ACTIVITY.getValue(), transactionDate,
+                        transactionAmount, null, interestPortion, feeChargesPortion, penaltyChargesPortion, null, false, null, externalId)
+                : null;
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index a747373c7..08040cb1e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -95,8 +95,6 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.LoanWithdra
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPostBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanChargeOffPreBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanDisbursalTransactionBusinessEvent;
-import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPostBusinessEvent;
-import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionAccrualActivityPreBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPostBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionInterestPaymentWaiverPreBusinessEvent;
@@ -230,7 +228,6 @@ import org.springframework.transaction.annotation.Transactional;
 @RequiredArgsConstructor
 public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService {
 
-    private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
     private final PlatformSecurityContext context;
     private final LoanTransactionValidator loanTransactionValidator;
     private final LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer;
@@ -499,9 +496,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             createNote(loan, command, changes);
             // auto create standing instruction
             createStandingInstruction(loan);
-
-            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-            loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
         }
 
         final Set<LoanCharge> loanCharges = loan.getActiveCharges();
@@ -526,8 +520,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
         }
         updateRecurringCalendarDatesForInterestRecalculation(loan);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
 
         // Post Dated Checks
@@ -551,6 +545,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalTransactionBusinessEvent(disbursalTransaction));
         }
 
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         return new CommandProcessingResultBuilder() //
                 .withCommandId(command.commandId()) //
                 .withEntityId(loan.getId()) //
@@ -869,8 +866,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
                 this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO);
             }
             updateRecurringCalendarDatesForInterestRecalculation(loan);
-            loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                    loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+            loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                    loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), true);
             loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
             businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalBusinessEvent(loan));
         }
@@ -1085,8 +1082,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
     private ChangedTransactionDetail recalculateLoanWithInterestPaymentWaiverTxn(Loan loan,
             LoanTransaction newInterestPaymentWaiverTransaction) {
         LocalDate recalculateFrom = null;
+        LocalDate transactionDate = newInterestPaymentWaiverTransaction.getTransactionDate();
         if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
-            recalculateFrom = newInterestPaymentWaiverTransaction.getTransactionDate();
+            recalculateFrom = transactionDate;
         }
         final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom, null);
 
@@ -1102,14 +1100,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
 
         final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = loan.getTransactionProcessor();
 
-        final LoanRepaymentScheduleInstallment currentInstallment = loan
-                .fetchLoanRepaymentScheduleInstallmentByDueDate(newInterestPaymentWaiverTransaction.getTransactionDate());
+        final LoanRepaymentScheduleInstallment currentInstallment = loan.fetchLoanRepaymentScheduleInstallmentByDueDate(transactionDate);
 
         boolean reprocess = true;
-
-        if (!loan.isForeclosure() && isTransactionChronologicallyLatest
-                && DateUtils.isEqualBusinessDate(newInterestPaymentWaiverTransaction.getTransactionDate()) && currentInstallment != null
-                && currentInstallment.getTotalOutstanding(loan.getCurrency())
+        if (!loan.isForeclosure() && isTransactionChronologicallyLatest && DateUtils.isEqualBusinessDate(transactionDate)
+                && currentInstallment != null && currentInstallment.getTotalOutstanding(loan.getCurrency())
                         .isEqualTo(newInterestPaymentWaiverTransaction.getAmount(loan.getCurrency()))) {
             reprocess = false;
         }
@@ -1155,66 +1150,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         return loan.reprocessTransactions();
     }
 
-    @Transactional
-    @Override
-    public Loan reverseReplayAccrualActivityTransaction(Loan loan, final LoanTransaction loanTransaction,
-            final LoanRepaymentScheduleInstallment installment, final LocalDate transactionDate) {
-
-        LoanTransaction newLoanTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
-                transactionDate);
-        if (!newLoanTransaction.getDateOf().isEqual(loanTransaction.getDateOf())
-                || !LoanTransaction.transactionAmountsMatch(loan.getCurrency(), loanTransaction, newLoanTransaction)) {
-            loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loanTransaction.getLoan(), loanTransaction,
-                    "reversed");
-            loanTransaction.reverse();
-            loanTransaction.updateExternalId(null);
-            newLoanTransaction.copyLoanTransactionRelations(loanTransaction.getLoanTransactionRelations());
-            newLoanTransaction.getLoanTransactionRelations().add(LoanTransactionRelation.linkToTransaction(newLoanTransaction,
-                    loanTransaction, LoanTransactionRelationTypeEnum.REPLAYED));
-            loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction);
-            loan.addLoanTransaction(newLoanTransaction);
-
-            LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction);
-            data.setNewTransactionDetail(newLoanTransaction);
-            businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
-        }
-        return loan;
-    }
-
-    @Transactional
-    @Override
-    public Loan makeAccrualActivityTransaction(Loan loan, final LoanRepaymentScheduleInstallment installment,
-            final LocalDate transactionDate) {
-
-        LoanTransaction newAccrualActivityTransaction = loanTransactionAssembler.assembleAccrualActivityTransaction(loan, installment,
-                transactionDate);
-
-        if (newAccrualActivityTransaction.getAmount().compareTo(BigDecimal.ZERO) == 0) {
-            return loan;
-        }
-
-        return makeAccrualActivityTransaction(loan, newAccrualActivityTransaction);
-    }
-
-    @Transactional
-    @Override
-    public Loan makeAccrualActivityTransaction(Loan loan, LoanTransaction newAccrualActivityTransaction) {
-        businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionAccrualActivityPreBusinessEvent(loan));
-
-        loanAccountDomainService.saveLoanTransactionWithDataIntegrityViolationChecks(newAccrualActivityTransaction);
-
-        loan.addLoanTransaction(newAccrualActivityTransaction);
-
-        businessEventNotifierService
-                .notifyPostBusinessEvent(new LoanTransactionAccrualActivityPostBusinessEvent(newAccrualActivityTransaction));
-
-        return loan;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult makeInterestPaymentWaiver(final JsonCommand command) {
-
         loanTransactionValidator.validateLoanTransactionInterestPaymentWaiver(command);
 
         final Long loanId = command.getLoanId();
@@ -1245,10 +1183,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
 
         saveNote(noteText, loan, newInterestPaymentWaiverTransaction);
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         loanAccountDomainService.setLoanDelinquencyTag(loan, newInterestPaymentWaiverTransaction.getTransactionDate());
 
         // disable all active standing orders linked to this loan if status changes to closed
@@ -1264,6 +1200,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
         businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent);
 
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         return new CommandProcessingResultBuilder().withCommandId(command.commandId()) //
                 .withLoanId(loan.getId()) //
                 .withEntityId(newInterestPaymentWaiverTransaction.getId()) //
@@ -1564,11 +1503,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             loan.updateLoanSummaryAndStatus();
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
 
         this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
 
@@ -1586,6 +1522,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
         businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData));
 
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         return new CommandProcessingResultBuilder() //
                 .withCommandId(command.commandId()) //
                 .withEntityId(entityId) //
@@ -1647,9 +1586,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             writeOffTransaction.reverse();
         }
 
-        if (loan.isClosedObligationsMet() || loan.isClosedWrittenOff() || loan.isClosedWithOutstandingAmountMarkedForReschedule()) {
-            loanLifecycleStateMachine.transition(LoanEvent.LOAN_ADJUST_TRANSACTION, loan);
-        }
+        loan.updateLoanSummaryAndStatus();
 
         if (newTransactionDetail.isRepaymentLikeType() || newTransactionDetail.isInterestWaiver()) {
             changedTransactionDetail = loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan,
@@ -1850,14 +1787,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             this.noteRepository.save(note);
         }
 
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
 
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
         businessEventNotifierService.notifyPostBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction));
+
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
         return new CommandProcessingResultBuilder() //
                 .withCommandId(command.commandId()) //
                 .withEntityId(waiveInterestTransaction.getId()) //
@@ -1918,44 +1856,48 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING);
         loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.WRITE_OFF_OUTSTANDING,
                 writtenOffOnLocalDate);
-        final ChangedTransactionDetail changedTransactionDetail = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
-                existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
 
-        loanAccrualsProcessingService.reprocessExistingAccruals(loan);
-        if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
-            loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
-        }
-
-        LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
-        this.loanTransactionRepository.saveAndFlush(writeOff);
-        for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
-            this.loanTransactionRepository.save(mapEntry.getValue());
-            this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
-        }
-        saveLoanWithDataIntegrityViolationChecks(loan);
-        final String noteText = command.stringValueOfParameterNamed("note");
-        if (StringUtils.isNotBlank(noteText)) {
-            changes.put("note", noteText);
-            final Note note = Note.loanTransactionNote(loan, writeOff, noteText);
-            this.noteRepository.save(note);
-        }
-
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
-        loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
-        businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
-        businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff));
-        return new CommandProcessingResultBuilder() //
+        CommandProcessingResultBuilder builder = new CommandProcessingResultBuilder() //
                 .withCommandId(command.commandId()) //
-                .withEntityId(writeOff.getId()) //
-                .withEntityExternalId(writeOff.getExternalId()) //
                 .withOfficeId(loan.getOfficeId()) //
                 .withClientId(loan.getClientId()) //
                 .withGroupId(loan.getGroupId()) //
                 .withLoanId(loanId) //
-                .with(changes).build();
+                .with(changes);
+
+        final ChangedTransactionDetail changedTransactionDetail = closeAsWrittenOff(loan, command, loanLifecycleStateMachine, changes,
+                existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
+
+        if (changedTransactionDetail != null) {
+            loanAccrualsProcessingService.reprocessExistingAccruals(loan);
+            if (loan.getLoanRepaymentScheduleDetail().isInterestRecalculationEnabled()) {
+                loanAccrualsProcessingService.processIncomePostingAndAccruals(loan);
+            }
+            LoanTransaction writeOff = changedTransactionDetail.getNewTransactionMappings().remove(0L);
+            this.loanTransactionRepository.saveAndFlush(writeOff);
+            for (final Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
+                this.loanTransactionRepository.save(mapEntry.getValue());
+                this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
+            }
+            saveLoanWithDataIntegrityViolationChecks(loan);
+            final String noteText = command.stringValueOfParameterNamed("note");
+            if (StringUtils.isNotBlank(noteText)) {
+                changes.put("note", noteText);
+                final Note note = Note.loanTransactionNote(loan, writeOff, noteText);
+                this.noteRepository.save(note);
+            }
+            loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                    loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+            loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+            businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+            businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(writeOff));
+
+            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+            loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
+            builder.withEntityId(writeOff.getId()).withEntityExternalId(writeOff.getExternalId());
+        }
+        return builder.build();
     }
 
     @Transactional
@@ -2020,12 +1962,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             this.noteRepository.save(note);
         }
 
-        if (possibleClosingTransaction != null) {
-            postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
-        }
         loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
 
         loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
 
@@ -2037,6 +1976,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         // disable all active standing instructions linked to the loan
         this.loanAccountDomainService.disableStandingInstructionsLinkedToClosedLoan(loan);
 
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         CommandProcessingResult result;
         if (possibleClosingTransaction != null) {
 
@@ -2654,15 +2596,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         }
         loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
 
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+
+        businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
+        businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
+
+        this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
         postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
         loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
-        if (writeOffTransaction != null) {
-            businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
-            businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction));
-        }
-        this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
         return new CommandProcessingResultBuilder() //
                 .withOfficeId(loan.getOfficeId()) //
                 .withClientId(loan.getClientId()) //
@@ -2811,11 +2755,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
             createLoanScheduleArchive(loan, scheduleGeneratorDTO);
         }
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+        this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
         postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
         loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
-        this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
         return new CommandProcessingResultBuilder() //
                 .withOfficeId(loan.getOfficeId()) //
                 .withClientId(loan.getClientId()) //
@@ -2879,11 +2825,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
         }
         loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
+        businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan));
+
         postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
         loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
-        businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan));
         return loan;
     }
 
@@ -3396,8 +3343,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         }
 
         loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
-        // Create journal entries for the new transaction(s)
-        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
         // Calculate and apply delinquency tag (if applicable)
         loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate);
         // disable all active standing orders linked to this loan if status
@@ -3414,11 +3359,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
             loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, refundTransaction);
         }
         // Raise business events
-        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
-        loanAccrualsProcessingService.processAccrualsForInterestRecalculation(loan,
-                loan.repaymentScheduleDetail().isInterestRecalculationEnabled());
+        loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan,
+                loan.repaymentScheduleDetail().isInterestRecalculationEnabled(), false);
         businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan));
 
+        // Create journal entries for the new transaction(s)
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds);
+
         Long entityId = refundTransaction.getId();
         ExternalId entityExternalId = refundTransaction.getExternalId();
         Long subEntityId = interestRefundTransaction != null ? interestRefundTransaction.getId() : null;
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 fdfaa6076..1f0605de6 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
@@ -100,6 +100,7 @@ import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoReadPlat
 import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.GLIMAccountInfoWritePlatformServiceImpl;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualActivityProcessingService;
+import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualEventService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventServiceImpl;
 import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService;
@@ -332,9 +333,16 @@ public class LoanAccountConfiguration {
     @Bean
     @ConditionalOnMissingBean(LoanStatusChangePlatformService.class)
     public LoanStatusChangePlatformService loanStatusChangePlatformService(BusinessEventNotifierService businessEventNotifierService,
+            LoanAccrualActivityProcessingService loanAccrualActivityProcessingService) {
+        return new LoanStatusChangePlatformServiceImpl(businessEventNotifierService, loanAccrualActivityProcessingService);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(LoanAccrualEventService.class)
+    public LoanAccrualEventService loanAccrualEventService(BusinessEventNotifierService businessEventNotifierService,
             LoanAccrualsProcessingService loanAccrualsProcessingService,
             LoanAccrualActivityProcessingService loanAccrualActivityProcessingService) {
-        return new LoanStatusChangePlatformServiceImpl(businessEventNotifierService, loanAccrualsProcessingService,
+        return new LoanAccrualEventService(businessEventNotifierService, loanAccrualsProcessingService,
                 loanAccrualActivityProcessingService);
     }
 
@@ -351,8 +359,8 @@ public class LoanAccountConfiguration {
 
     @Bean
     @ConditionalOnMissingBean(LoanWritePlatformService.class)
-    public LoanWritePlatformService loanWritePlatformService(LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory,
-            PlatformSecurityContext context, LoanTransactionValidator loanTransactionValidator,
+    public LoanWritePlatformService loanWritePlatformService(PlatformSecurityContext context,
+            LoanTransactionValidator loanTransactionValidator,
             LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper,
             LoanAccountDomainService loanAccountDomainService, NoteRepository noteRepository,
             LoanTransactionRepository loanTransactionRepository, LoanTransactionRelationRepository loanTransactionRelationRepository,
@@ -381,18 +389,17 @@ public class LoanAccountConfiguration {
             LoanOfficerValidator loanOfficerValidator, LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator,
             LoanDisbursementService loanDisbursementService, LoanScheduleService loanScheduleService,
             LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService) {
-        return new LoanWritePlatformServiceJpaRepositoryImpl(transactionProcessorFactory, context, loanTransactionValidator,
-                loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository,
-                loanTransactionRepository, loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService,
-                calendarInstanceRepository, paymentDetailWritePlatformService, holidayRepository, configurationDomainService,
-                workingDaysRepository, accountTransfersWritePlatformService, accountTransfersReadPlatformService,
-                accountAssociationsReadPlatformService, loanReadPlatformService, fromApiJsonHelper, calendarRepository,
-                loanScheduleHistoryWritePlatformService, loanApplicationValidator, accountAssociationRepository,
-                accountTransferDetailRepository, businessEventNotifierService, guarantorDomainService, loanUtilService, loanSummaryWrapper,
-                entityDatatableChecksWritePlatformService, transactionProcessingStrategy, codeValueRepository,
-                cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler,
-                postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, defaultLoanLifecycleStateMachine,
-                loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
+        return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer,
+                loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository,
+                loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository,
+                paymentDetailWritePlatformService, holidayRepository, configurationDomainService, workingDaysRepository,
+                accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService,
+                loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService,
+                loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService,
+                guarantorDomainService, loanUtilService, loanSummaryWrapper, entityDatatableChecksWritePlatformService,
+                transactionProcessingStrategy, codeValueRepository, cashierTransactionDataValidator, glimRepository, loanRepository,
+                repaymentWithPostDatedChecksAssembler, postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository,
+                defaultLoanLifecycleStateMachine, loanAccountLockService, externalIdFactory, replayedTransactionBusinessEventService,
                 loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, accountTransferRepository,
                 loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator,
                 loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java
index b593f2b3a..17f633d3e 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java
@@ -128,6 +128,9 @@ class LoanChargeWritePlatformServiceImplTest {
     @Mock
     private LoanChargeValidator loanChargeValidator;
 
+    @Mock
+    private LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
+
     @BeforeEach
     void setUp() {
         when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
index 3f3923e8e..4574dc5d8 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImplTest.java
@@ -240,8 +240,6 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
         when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
         doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
         when(loan.isClosedWrittenOff()).thenReturn(false);
-        when(loan.isClosedObligationsMet()).thenReturn(false);
-        when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
         when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
 
         // Act
@@ -287,8 +285,6 @@ class LoanWritePlatformServiceJpaRepositoryImplTest {
         when(loan.findExistingReversedTransactionIds()).thenReturn(Collections.emptyList());
         doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any());
         when(loan.isClosedWrittenOff()).thenReturn(false);
-        when(loan.isClosedObligationsMet()).thenReturn(false);
-        when(loan.isClosedWithOutstandingAmountMarkedForReschedule()).thenReturn(false);
         when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true);
 
         // Act
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 c494a0595..b164f88a7 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
@@ -553,7 +553,7 @@ public abstract class BaseLoanIntegrationTest {
     protected void verifyTransactions(Long loanId, Transaction... transactions) {
         GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
         if (transactions == null || transactions.length == 0) {
-            Assertions.assertTrue(loanDetails.getTransactions().isEmpty(), "No transaction is expected");
+            Assertions.assertTrue(loanDetails.getTransactions().isEmpty(), "No transaction is expected on loan " + loanId);
         } else {
             Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
             Arrays.stream(transactions).forEach(tr -> {
@@ -562,12 +562,13 @@ public abstract class BaseLoanIntegrationTest {
                                 && Objects.equals(item.getType().getValue(), tr.type) //
                                 && Objects.equals(item.getDate(), LocalDate.parse(tr.date, dateTimeFormatter)))
                         .findFirst();
-                Assertions.assertTrue(optTx.isPresent(), "Required transaction  not found: " + tr);
+                Assertions.assertTrue(optTx.isPresent(), "Required transaction  not found: " + tr + " on loan " + loanId);
 
                 GetLoansLoanIdTransactions tx = optTx.get();
 
                 if (tr.reversed != null) {
-                    Assertions.assertEquals(tr.reversed, tx.getManuallyReversed(), "Transaction is not reversed: " + tr);
+                    Assertions.assertEquals(tr.reversed, tx.getManuallyReversed(),
+                            "Transaction is not reversed: " + tr + " on loan " + loanId);
                 }
             });
         }
@@ -576,9 +577,9 @@ public abstract class BaseLoanIntegrationTest {
     protected void verifyTransactions(Long loanId, TransactionExt... transactions) {
         GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
         if (transactions == null || transactions.length == 0) {
-            assertNull(loanDetails.getTransactions(), "No transaction is expected");
+            assertNull(loanDetails.getTransactions(), "No transaction is expected on loan " + loanId);
         } else {
-            Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size());
+            Assertions.assertEquals(transactions.length, loanDetails.getTransactions().size(), "Number of transactions on loan " + loanId);
             Arrays.stream(transactions).forEach(tr -> {
                 boolean found = loanDetails.getTransactions().stream().anyMatch(item -> Objects.equals(item.getAmount(), tr.amount) //
                         && Objects.equals(item.getType().getValue(), tr.type) //
@@ -591,7 +592,7 @@ public abstract class BaseLoanIntegrationTest {
                         && Objects.equals(item.getOverpaymentPortion(), tr.overpaymentPortion) //
                         && Objects.equals(item.getUnrecognizedIncomePortion(), tr.unrecognizedPortion) //
                 );
-                Assertions.assertTrue(found, "Required transaction not found: " + tr);
+                Assertions.assertTrue(found, "Required transaction not found: " + tr + " on loan " + loanId);
             });
         }
     }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java
index 5b7de1503..a217e704b 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeProgressiveTest.java
@@ -27,6 +27,7 @@ import org.apache.fineract.client.models.PostLoanProductsResponse;
 import org.apache.fineract.client.models.PostLoansResponse;
 import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants;
 import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -78,15 +79,20 @@ public class LoanChargeProgressiveTest extends BaseLoanIntegrationTest {
             addLoanCharge(loanId, chargeResponse.getResourceId(), "03 October 2024", 20.0d);
             final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
             Assertions.assertTrue(
-                    loanDetails.getTransactions().stream().anyMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
+                    loanDetails.getTransactions().stream().noneMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
         });
         runAt("04 October 2024", () -> {
             globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY,
                     false);
             executeInlineCOB(loanId);
             final GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
-            Assertions.assertTrue(
-                    loanDetails.getTransactions().stream().anyMatch(t -> t.getType().getAccrual() && t.getAmount().equals(20.0d)));
+            Assertions.assertTrue(loanDetails.getTransactions().stream()
+                    .anyMatch(t -> t.getType().getAccrual() && t.getFeeChargesPortion().equals(20.0d)));
         });
     }
+
+    @AfterEach
+    public void afterEach() {
+        globalConfigurationHelper.manageConfigurations(GlobalConfigurationConstants.ENABLE_IMMEDIATE_CHARGE_ACCRUAL_POST_MATURITY, false);
+    }
 }
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java
index 70b42eecb..696fafe63 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargebackWithCreditAllocationsIntegrationTests.java
@@ -300,6 +300,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
                     transaction(312.0, "Repayment", "20 February 2023", 626.0, 312.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
                     transaction(312.0, "Repayment", "20 March 2023", 314.0, 312.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
                     transaction(384.0, "Repayment", "20 April 2023", 0.0, 314.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
+                    transaction(70.0, "Accrual", "20 April 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
                     transaction(100.0, "Chargeback", "02 May 2023", 30.0, 30.0, 0.0, 50.0, 20.0, 0.0, 0.0) //
             );
 
@@ -584,6 +585,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
             verifyTransactions(loanId, //
                     transaction(1250.0, "Disbursement", "01 January 2023", 1250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
                     transaction(1370.0, "Repayment", "20 January 2023", 0, 1250.0, 0.0, 50.0, 20.0, 0.0, 50.0), //
+                    transaction(70.0, "Accrual", "20 January 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
                     transaction(100.0, "Chargeback", "02 May 2023", 50.0, 100.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
             );
 
@@ -649,6 +651,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
             verifyTransactions(loanId, //
                     transaction(1250.0, "Disbursement", "01 January 2023", 1250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
                     transaction(1370.0, "Repayment", "20 January 2023", 0, 1250.0, 0.0, 50.0, 20.0, 0.0, 50.0), //
+                    transaction(70.0, "Accrual", "20 January 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
                     transaction(100.0, "Chargeback", "20 January 2023", 30.0, 30.0, 0.0, 50.0, 20.0, 0.0, 50.0) //
             );
 
@@ -715,6 +718,7 @@ public class LoanChargebackWithCreditAllocationsIntegrationTests extends BaseLoa
             verifyTransactions(loanId, //
                     transaction(1250.0, "Disbursement", "01 January 2023", 1250.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
                     transaction(1370.0, "Repayment", "20 January 2023", 0, 1250.0, 0.0, 50.0, 20.0, 0.0, 50.0), //
+                    transaction(70.0, "Accrual", "20 January 2023", 0.0, 0.0, 0.0, 50.0, 20.0, 0.0, 0.0), //
                     transaction(100.0, "Chargeback", "20 January 2023", 0.0, 30.0, 0.0, 50.0, 20.0, 0.0, 50.0) //
             );
 
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
index 3af858d15..c9cc50765 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java
@@ -210,9 +210,11 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
-                    transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), transaction(5.74, "Accrual", "22 January 2021"),
-                    transaction(5.74, "Interest Refund", "22 January 2021"));
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), //
+                    transaction(5.74, "Accrual", "22 January 2021"), //
+                    transaction(5.74, "Interest Refund", "22 January 2021") //
+            );
         });
     }
 
@@ -241,9 +243,11 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
-                    transaction(1000.0, "Payout Refund", "22 January 2021"), transaction(5.74, "Accrual", "22 January 2021"),
-                    transaction(5.74, "Interest Refund", "22 January 2021"));
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(1000.0, "Payout Refund", "22 January 2021"), //
+                    transaction(5.74, "Accrual", "22 January 2021"), //
+                    transaction(5.74, "Interest Refund", "22 January 2021") //
+            );
         });
     }
 
@@ -272,9 +276,10 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
-                    transaction(1000.0, "Payout Refund", "01 February 2021"), transaction(8.48, "Accrual", "01 February 2021"),
-                    transaction(8.48, "Interest Refund", "01 February 2021"));
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(1000.0, "Payout Refund", "01 February 2021"), //
+                    transaction(8.48, "Accrual", "01 February 2021"), //
+                    transaction(8.48, "Interest Refund", "01 February 2021")); //
         });
     }
 
@@ -315,9 +320,12 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"),
-                    transaction(87.89, "Repayment", "01 February 2021"), transaction(1000.0, "Payout Refund", "09 February 2021"),
-                    transaction(10.49, "Interest Refund", "09 February 2021"), transaction(10.49, "Accrual", "09 February 2021"));
+            verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
+                    transaction(87.89, "Repayment", "01 February 2021"), //
+                    transaction(1000.0, "Payout Refund", "09 February 2021"), //
+                    transaction(10.49, "Interest Refund", "09 February 2021"), //
+                    transaction(10.49, "Accrual", "09 February 2021") //
+            );
         });
     }
 
@@ -350,9 +358,12 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 January 2021"),
-                    transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"),
-                    transaction(5.74, "Accrual", "22 January 2021"), transaction(5.74, "Interest Refund", "22 January 2021"));
+            verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 January 2021"), //
+                    transaction(250.0, "Disbursement", "01 January 2021"), //
+                    transaction(1000.0, "Payout Refund", "22 January 2021"), //
+                    transaction(5.74, "Accrual", "22 January 2021"), //
+                    transaction(5.74, "Interest Refund", "22 January 2021") //
+            );
         });
     }
 
@@ -386,9 +397,12 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 January 2021"),
-                    transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"),
-                    transaction(5.13, "Accrual", "22 January 2021"), transaction(5.13, "Interest Refund", "22 January 2021"));
+            verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 January 2021"), //
+                    transaction(250.0, "Disbursement", "01 January 2021"), //
+                    transaction(1000.0, "Payout Refund", "22 January 2021"), //
+                    transaction(5.13, "Accrual", "22 January 2021"), //
+                    transaction(5.13, "Interest Refund", "22 January 2021") //
+            );
         });
     }
 
@@ -435,10 +449,13 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
             Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
 
             logLoanTransactions(loanId);
-            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"),
-                    transaction(500.0, "Disbursement", "07 January 2021"), transaction(1000.0, "Payout Refund", "09 February 2021"),
-                    transaction(87.82, "Repayment", "01 February 2021"), transaction(9.67, "Interest Refund", "09 February 2021"),
-                    transaction(9.67, "Accrual", "09 February 2021"));
+            verifyTransactions(loanId, transaction(500.0, "Disbursement", "01 January 2021"), //
+                    transaction(500.0, "Disbursement", "07 January 2021"), //
+                    transaction(1000.0, "Payout Refund", "09 February 2021"), //
+                    transaction(87.82, "Repayment", "01 February 2021"), //
+                    transaction(9.67, "Interest Refund", "09 February 2021"), //
+                    transaction(9.67, "Accrual", "09 February 2021") //
+            );
         });
     }
 
@@ -1115,9 +1132,10 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
 
             verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
                     transaction(85.63, "Repayment", "10 January 2021"), //
-                    transaction(5.7, "Accrual", "22 January 2021"), //
+                    transaction(5.70, "Accrual", "22 January 2021"), //
                     transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), //
-                    transaction(5.42, "Interest Refund", "22 January 2021") //
+                    transaction(5.42, "Interest Refund", "22 January 2021"), //
+                    transaction(0.28, "Accrual Adjustment", "22 January 2021") //
             );
         });
     }
@@ -1202,7 +1220,8 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
                     reversedTransaction(85.63, "Repayment", "10 January 2021"), //
                     transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), //
                     transaction(5.70, "Interest Refund", "22 January 2021"), //
-                    transaction(5.42, "Accrual", "22 January 2021") //
+                    transaction(5.42, "Accrual", "22 January 2021"), //
+                    transaction(0.28, "Accrual", "22 January 2021") //
             );
 
         });
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java
index a531b6374..622394ea8 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionAccrualActivityPostingTest.java
@@ -569,8 +569,8 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
             verifyTransactions(loanId.get(),
                     transaction(1000.0, "Disbursement", disbursementDay, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
                     transaction(1070.0, "Repayment", repaymentDate1, 0.0, 1000.0, 0.0, 40.0, 30, 0.0, 0.0, true), //
+                    transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false)); //
-
         });
     }
 
@@ -652,32 +652,36 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
 
             addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
 
-            verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+            verifyTransactions(loanId.get(),
+                    transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
+                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
                     transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
-                    transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
-                    transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
-
+                    transaction(70.0, "Accrual Activity", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false) //
+            );
         });
         runAt(disbursementDay2, () -> {
 
             loanTransactionHelper.disburseLoan(loanId.get(), new PostLoansLoanIdRequest().actualDisbursementDate(disbursementDay2)
                     .dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(500.0)).locale("en"));
 
-            verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
-                    transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
-                    transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
-
+            verifyTransactions(loanId.get(),
+                    transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
+                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+                    transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
+                    transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false) //
+            );
         });
         runAt(repaymentPeriod1CloseDate, () -> {
 
             inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
 
-            verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+            verifyTransactions(loanId.get(),
+                    transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false), //
+                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
+                    transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
-                    transaction(70.0, "Accrual", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
-                    transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
-                    transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
-
+                    transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false) //
+            );
         });
     }
 
@@ -727,6 +731,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
 
             verifyTransactions(loanId.get(), transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 0.0, 40.0, 30.0, 0.0, 80.0, false), //
                     transaction(500.0, "Disbursement", disbursementDay2, 420.0, 0.0, 0.0, 0.0, 0.0, 0.0, 80.0, false), //
+                    transaction(70.0, "Accrual", repaymentDate1, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0.0, 0.0, 0.0, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(500.0, "Disbursement", disbursementDay, 500.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false)); //
 
@@ -828,6 +833,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
 
             verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
                     transaction(570, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
+                    transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
                     transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
                     transaction(500.0, "Disbursement", disbursementDay2, 500, 0, 0, 0, 0, 0, 0, false));
         });
@@ -837,9 +843,9 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
 
             verifyTransactions(loanId.get(), transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false),
                     transaction(570.0, "Repayment", repaymentDate1, 0.0, 500.0, 0, 40.0, 30.0, 0, 0, false),
+                    transaction(70.0, "Accrual", repaymentDate1, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
                     transaction(70.0, "Accrual Activity", repaymentPeriod1DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false),
-                    transaction(500.0, "Disbursement", disbursementDay2, 500.0, 0, 0, 0, 0, 0, 0, false),
-                    transaction(70.0, "Accrual", repaymentPeriod2DueDate, 0, 0, 0.0, 40.0, 30.0, 0, 0, false));
+                    transaction(500.0, "Disbursement", disbursementDay2, 500.0, 0, 0, 0, 0, 0, 0, false));
         });
     }
 
@@ -867,18 +873,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
             chargeFee(loanId.get(), 40.0, repaymentPeriod1DueDate);
             addRepaymentForLoan(loanId.get(), 650.0, repaymentDate1);
             verifyTransactions(loanId.get(),
-                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), // transaction(109.45,
-                                                                                                                       // "Accrual",
-                                                                                                                       // repaymentDate1,
-                                                                                                                       // 0.0,
-                                                                                                                       // 0,
-                                                                                                                       // 39.45,
-                                                                                                                       // 40.0,
-                                                                                                                       // 30.0,
-                                                                                                                       // 0,
-                                                                                                                       // 0,
-                                                                                                                       // false),
-                                                                                                                       // //
+                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), //
                     transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //
@@ -887,18 +882,7 @@ public class LoanTransactionAccrualActivityPostingTest extends BaseLoanIntegrati
         runAt(repaymentPeriod1CloseDate, () -> {
             inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.get()));
             verifyTransactions(loanId.get(),
-                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), // transaction(109.45,
-                                                                                                                       // "Accrual",
-                                                                                                                       // repaymentDate1,
-                                                                                                                       // 0.0,
-                                                                                                                       // 0,
-                                                                                                                       // 39.45,
-                                                                                                                       // 40.0,
-                                                                                                                       // 30.0,
-                                                                                                                       // 0,
-                                                                                                                       // 0,
-                                                                                                                       // false),
-                                                                                                                       // //
+                    transaction(650.0, "Repayment", repaymentDate1, 0.0, 500.0, 39.45, 40.0, 30.0, 0.0, 40.55, false), //
                     transaction(109.45, "Accrual Activity", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(109.45, "Accrual", repaymentDate1, 0.0, 0.0, 39.45, 40.0, 30.0, 0.0, 0.0, false), //
                     transaction(500.0, "Disbursement", disbursementDay, 500.0, 0, 0, 0, 0, 0, 0, false) //