FINERACT-2356: Fix incorrect accounting during write-off in case the loan was already charged-off
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
index 8029dd6..d2ba126 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/TransactionType.java
@@ -42,6 +42,7 @@
     BUY_DOWN_FEE_ADJUSTMENT("buyDownFeeAdjustment"), //
     BUY_DOWN_FEE_AMORTIZATION("buyDownFeeAmortization"), //
     INTEREST_REFUND("interestRefund"), //
+    WRITE_OFF("writeOff"), //
     ;
 
     public final String value;
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanWriteOff.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanWriteOff.feature
index cbaebe4..d24f592 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanWriteOff.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanWriteOff.feature
@@ -60,8 +60,64 @@
         | Close (as written-off) | 650.0  | 650.0     | 0.0      | 0.0  | 0.0       | 0.0          |
       Then Admin fails to undo "1"th transaction made on "29 January 2023"
 
+    @TestRailId:C4006
+    Scenario: Verify accounting during Write-off when loan was already charged-off
+      When Admin sets the business date to "1 January 2023"
+      And Admin creates a client with random data
+      When Admin creates a fully customized loan with the following data:
+        | LoanProduct       | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type  | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy                        |
+        | LP1_INTEREST_FLAT | 1 January 2023    | 1000           | 12                     | FLAT          | SAME_AS_REPAYMENT_PERIOD    | EQUAL_INSTALLMENTS | 3                 | MONTHS                | 1              | MONTHS                 | 3                  | 0                       | 0                      | 0                    | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
+      And Admin successfully approves the loan on "1 January 2023" with "1000" amount and expected disbursement date on "1 January 2023"
+      And Admin successfully disburse the loan on "1 January 2023" with "1000" EUR transaction amount
+      And Admin adds an NSF fee because of payment bounce with "1 January 2023" transaction date
+      When Admin sets the business date to "22 February 2023"
+      And Admin adds a 10 % Processing charge to the loan with "en" locale on date: "22 February 2023"
+      And Admin does charge-off the loan on "22 February 2023"
+      Then Loan marked as charged-off on "22 February 2023"
+      Then Loan status will be "ACTIVE"
+      Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2023" which has the following Journal entries:
+        | Type      | Account code | Account name              | Debit  | Credit |
+        | ASSET     | 112601       | Loans Receivable          | 1000.0 |        |
+        | LIABILITY | 145023       | Suspense/Clearing account |        | 1000.0 |
+      Then Loan Transactions tab has a "CHARGE_OFF" transaction with date "22 February 2023" which has the following Journal entries:
+        | Type    | Account code | Account name               | Debit  | Credit |
+        | ASSET   | 112601       | Loans Receivable           |        | 1000.0 |
+        | ASSET   | 112603       | Interest/Fee Receivable    |        | 143.0  |
+        | EXPENSE | 744007       | Credit Loss/Bad Debt       | 1000.0 |        |
+        | INCOME  | 404001       | Interest Income Charge Off | 30.0   |        |
+        | INCOME  | 404008       | Fee Charge Off             | 113.0  |        |
+      When Admin sets the business date to "1 March 2023"
+      And Admin does write-off the loan on "1 March 2023"
+      Then Loan status will be "CLOSED_WRITTEN_OFF"
+      Then Loan Transactions tab has a "WRITE_OFF" transaction with date "01 March 2023" which has the following Journal entries:
+        | Type    | Account code | Account name               | Debit  | Credit |
+        | EXPENSE | 744007       | Credit Loss/Bad Debt       |        | 1000.0 |
+        | INCOME  | 404001       | Interest Income Charge Off |        | 30.0   |
+        | INCOME  | 404008       | Fee Charge Off             |        | 113.0  |
+        | EXPENSE | e4           | Written off                | 1143.0 |        |
 
-
-
-
-
+    @TestRailId:C4007
+    Scenario: Verify accounting during Write-off when loan was not charged-off before
+      When Admin sets the business date to "1 January 2023"
+      And Admin creates a client with random data
+      When Admin creates a fully customized loan with the following data:
+        | LoanProduct       | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type  | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy                        |
+        | LP1_INTEREST_FLAT | 1 January 2023    | 1000           | 12                     | FLAT          | SAME_AS_REPAYMENT_PERIOD    | EQUAL_INSTALLMENTS | 3                 | MONTHS                | 1              | MONTHS                 | 3                  | 0                       | 0                      | 0                    | PENALTIES_FEES_INTEREST_PRINCIPAL_ORDER |
+      And Admin successfully approves the loan on "1 January 2023" with "1000" amount and expected disbursement date on "1 January 2023"
+      And Admin successfully disburse the loan on "1 January 2023" with "1000" EUR transaction amount
+      And Admin adds an NSF fee because of payment bounce with "1 January 2023" transaction date
+      When Admin sets the business date to "22 February 2023"
+      And Admin adds a 10 % Processing charge to the loan with "en" locale on date: "22 February 2023"
+      Then Loan status will be "ACTIVE"
+      Then Loan Transactions tab has a "DISBURSEMENT" transaction with date "01 January 2023" which has the following Journal entries:
+        | Type      | Account code | Account name              | Debit  | Credit |
+        | ASSET     | 112601       | Loans Receivable          | 1000.0 |        |
+        | LIABILITY | 145023       | Suspense/Clearing account |        | 1000.0 |
+      When Admin sets the business date to "1 March 2023"
+      And Admin does write-off the loan on "1 March 2023"
+      Then Loan status will be "CLOSED_WRITTEN_OFF"
+      Then Loan Transactions tab has a "WRITE_OFF" transaction with date "01 March 2023" which has the following Journal entries:
+        | Type    | Account code | Account name               | Debit  | Credit |
+        | ASSET   | 112601       | Loans Receivable           |        | 1000.0 |
+        | ASSET   | 112603       | Interest/Fee Receivable    |        | 143.0  |
+        | EXPENSE | e4           | Written off                | 1143.0 |        |
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
index c518f00..40ebb22 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
@@ -79,8 +79,7 @@
              */
             else if ((transactionType.isRepaymentType() && !transactionType.isChargeAdjustment())
                     || transactionType.isRepaymentAtDisbursement() || transactionType.isChargePayment()) {
-                createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false,
-                        transactionType.isRepaymentAtDisbursement());
+                createJournalEntriesForRepayments(loanDTO, loanTransactionDTO, office, transactionType.isRepaymentAtDisbursement());
             }
 
             // Logic for handling recovery payments
@@ -100,7 +99,7 @@
 
             // Handle Write Offs
             else if ((transactionType.isWriteOff() || transactionType.isWaiveInterest() || transactionType.isWaiveCharges())) {
-                createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, true, false);
+                createJournalEntriesForWriteOffs(loanDTO, loanTransactionDTO, office);
             }
 
             // Logic for Refunds of Active Loans
@@ -1168,35 +1167,32 @@
      *
      * <b>Penalty Repayment</b>: Debits "Fund Source" and Credits "Receivable Penalties" <br/>
      * <br/>
-     * Handles write offs using the following posting rules <br/>
-     * <br/>
-     * <b>Principal Write off</b>: Debits "Losses Written Off" and Credits "Loan Portfolio"<br/>
-     *
-     * <b>Interest Write off</b>:Debits "Losses Written off" and Credits "Receivable Interest" <br/>
-     *
-     * <b>Fee Write off</b>:Debits "Losses Written off" and Credits "Receivable Fees" <br/>
-     *
-     * <b>Penalty Write off</b>: Debits "Losses Written off" and Credits "Receivable Penalties" <br/>
-     * <br/>
-     * <br/>
      *
      * @param loanTransactionDTO
      * @param loanDTO
      * @param office
      */
-    private void createJournalEntriesForRepaymentsAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
-            final Office office, final boolean writeOff, final boolean isIncomeFromFee) {
+    private void createJournalEntriesForRepayments(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office,
+            final boolean isIncomeFromFee) {
         final boolean isMarkedChargeOff = loanDTO.isMarkedAsChargeOff();
         if (isMarkedChargeOff) {
-            createJournalEntriesForChargeOffLoanRepaymentAndWriteOffs(loanDTO, loanTransactionDTO, office, writeOff, isIncomeFromFee);
-
+            createJournalEntriesForRepaymentWhenLoanIsChargedOff(loanDTO, loanTransactionDTO, office, isIncomeFromFee);
         } else {
-            createJournalEntriesForLoansRepaymentAndWriteOffs(loanDTO, loanTransactionDTO, office, writeOff, isIncomeFromFee);
+            createJournalEntriesForLoanRepayments(loanDTO, loanTransactionDTO, office, isIncomeFromFee);
         }
     }
 
-    private void createJournalEntriesForChargeOffLoanRepaymentAndWriteOffs(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO,
-            Office office, boolean writeOff, boolean isIncomeFromFee) {
+    private void createJournalEntriesForWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO, final Office office) {
+        final boolean isMarkedChargeOff = loanDTO.isMarkedAsChargeOff();
+        if (isMarkedChargeOff) {
+            createJournalEntriesForWriteOffsWhenLoanIsChargedOff(loanDTO, loanTransactionDTO, office);
+        } else {
+            createJournalEntriesForLoanWriteOffs(loanDTO, loanTransactionDTO, office);
+        }
+    }
+
+    private void createJournalEntriesForRepaymentWhenLoanIsChargedOff(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
+            final Office office, final boolean isIncomeFromFee) {
         // loan properties
         final Long loanProductId = loanDTO.getLoanProductId();
         final Long loanId = loanDTO.getLoanId();
@@ -1212,7 +1208,7 @@
         final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
         final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
         final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
-        GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
+        final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
 
         BigDecimal totalDebitAmount = new BigDecimal(0);
 
@@ -1240,17 +1236,14 @@
                             AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
                             glAccountBalanceHolder);
                 }
-
             } else if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
                         AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(),
                         glAccountBalanceHolder);
-
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
                         AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
                         glAccountBalanceHolder);
-
             } else {
                 populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
                         AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
@@ -1314,7 +1307,7 @@
                     this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode,
                             AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(), loanProductId, loanId, transactionId, transactionDate,
                             feesAmount, loanTransactionDTO.getFeePayments());
-                    GLAccount debitAccount = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    final GLAccount debitAccount = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                             AccrualAccountsForLoan.FUND_SOURCE.getValue(), paymentTypeId);
                     glAccountBalanceHolder.addToDebit(debitAccount, feesAmount);
 
@@ -1322,7 +1315,6 @@
                     populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
                             AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
                 }
-
             }
 
         }
@@ -1389,30 +1381,25 @@
         // create credit entries
         for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
             if (MathUtil.isGreaterThanZero(creditEntry.getValue())) {
-                GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
+                final GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
                 this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
                         creditEntry.getValue(), glAccount);
             }
         }
 
         if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
-            if (writeOff) {
-                this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(),
+            if (loanTransactionDTO.isLoanToLoanTransfer()) {
+                this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId,
+                        paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
+            } else if (loanTransactionDTO.isAccountTransfer()) {
+                this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
                         loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
             } else {
-                if (loanTransactionDTO.isLoanToLoanTransfer()) {
-                    this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(),
-                            loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
-                } else if (loanTransactionDTO.isAccountTransfer()) {
-                    this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
-                            loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
-                } else {
-                    // create debit entries
-                    for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
-                        GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
-                        this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
-                                debitEntry.getValue(), glAccount);
-                    }
+                // create debit entries
+                for (Map.Entry<Long, BigDecimal> debitEntry : glAccountBalanceHolder.getDebitBalances().entrySet()) {
+                    final GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
+                    this.helper.createDebitJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
+                            debitEntry.getValue(), glAccount);
                 }
             }
         }
@@ -1423,7 +1410,8 @@
          ***/
         if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
             if (loanTransactionDTO.getTransactionType().isChargeRefund()) {
-                Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
+                final Integer incomeAccount = this.helper
+                        .getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
                 this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
                         loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
             }
@@ -1431,8 +1419,87 @@
 
     }
 
-    private void createJournalEntriesForLoansRepaymentAndWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
-            final Office office, final boolean writeOff, final boolean isIncomeFromFee) {
+    private void createJournalEntriesForWriteOffsWhenLoanIsChargedOff(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
+            final Office office) {
+        // loan properties
+        final Long loanProductId = loanDTO.getLoanProductId();
+        final Long loanId = loanDTO.getLoanId();
+        final String currencyCode = loanDTO.getCurrencyCode();
+        final boolean isMarkedFraud = loanDTO.isMarkedAsFraud();
+
+        // transaction properties
+        final String transactionId = loanTransactionDTO.getTransactionId();
+        final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
+        final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
+        final BigDecimal interestAmount = loanTransactionDTO.getInterest();
+        final BigDecimal feesAmount = loanTransactionDTO.getFees();
+        final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
+        final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
+        final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
+        final GLAccountBalanceHolder glAccountBalanceHolder = new GLAccountBalanceHolder();
+
+        BigDecimal totalDebitAmount = new BigDecimal(0);
+
+        // principal payment
+        if (MathUtil.isGreaterThanZero(principalAmount)) {
+            totalDebitAmount = totalDebitAmount.add(principalAmount);
+            if (isMarkedFraud) {
+                populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId,
+                        AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
+                        glAccountBalanceHolder);
+            } else {
+                populateCreditDebitMaps(loanProductId, principalAmount, paymentTypeId, AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(),
+                        AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
+            }
+        }
+
+        // interest payment
+        if (MathUtil.isGreaterThanZero(interestAmount)) {
+            totalDebitAmount = totalDebitAmount.add(interestAmount);
+            populateCreditDebitMaps(loanProductId, interestAmount, paymentTypeId,
+                    AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
+                    glAccountBalanceHolder);
+        }
+
+        // handle fees payment
+        if (MathUtil.isGreaterThanZero(feesAmount)) {
+            totalDebitAmount = totalDebitAmount.add(feesAmount);
+            populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(),
+                    AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
+        }
+
+        // handle penalties
+        if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
+            totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
+            populateCreditDebitMaps(loanProductId, penaltiesAmount, paymentTypeId,
+                    AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), AccrualAccountsForLoan.FUND_SOURCE.getValue(),
+                    glAccountBalanceHolder);
+        }
+
+        // overpayment
+        if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
+            totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
+            populateCreditDebitMaps(loanProductId, overPaymentAmount, paymentTypeId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
+                    AccrualAccountsForLoan.FUND_SOURCE.getValue(), glAccountBalanceHolder);
+        }
+
+        // create credit entries
+        for (Map.Entry<Long, BigDecimal> creditEntry : glAccountBalanceHolder.getCreditBalances().entrySet()) {
+            if (MathUtil.isGreaterThanZero(creditEntry.getValue())) {
+                final GLAccount glAccount = glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
+                this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate,
+                        creditEntry.getValue(), glAccount);
+            }
+        }
+
+        if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
+            this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(),
+                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
+        }
+    }
+
+    private void createJournalEntriesForLoanRepayments(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
+            final Office office, final boolean isIncomeFromFee) {
         // loan properties
         final Long loanProductId = loanDTO.getLoanProductId();
         final Long loanId = loanDTO.getLoanId();
@@ -1450,13 +1517,13 @@
 
         BigDecimal totalDebitAmount = new BigDecimal(0);
 
-        Map<GLAccount, BigDecimal> accountMap = new LinkedHashMap<>();
-        Map<Integer, BigDecimal> debitAccountMapForGoodwillCredit = new LinkedHashMap<>();
+        final Map<GLAccount, BigDecimal> accountMap = new LinkedHashMap<>();
+        final Map<Integer, BigDecimal> debitAccountMapForGoodwillCredit = new LinkedHashMap<>();
 
-        // handle principal payment or writeOff
+        // handle principal payment
         if (MathUtil.isGreaterThanZero(principalAmount)) {
             totalDebitAmount = totalDebitAmount.add(principalAmount);
-            GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                     AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
             accountMap.put(account, principalAmount);
             if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
@@ -1465,13 +1532,13 @@
             }
         }
 
-        // handle interest payment of writeOff
+        // handle interest payment
         if (MathUtil.isGreaterThanZero(interestAmount)) {
             totalDebitAmount = totalDebitAmount.add(interestAmount);
-            GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                     AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId);
             if (accountMap.containsKey(account)) {
-                BigDecimal amount = accountMap.get(account).add(interestAmount);
+                final BigDecimal amount = accountMap.get(account).add(interestAmount);
                 accountMap.put(account, amount);
             } else {
                 accountMap.put(account, interestAmount);
@@ -1483,17 +1550,17 @@
             }
         }
 
-        // handle fees payment of writeOff
+        // handle fees payment
         if (MathUtil.isGreaterThanZero(feesAmount)) {
             totalDebitAmount = totalDebitAmount.add(feesAmount);
             if (isIncomeFromFee) {
                 this.helper.createCreditJournalEntryForLoanCharges(office, currencyCode, AccrualAccountsForLoan.INCOME_FROM_FEES.getValue(),
                         loanProductId, loanId, transactionId, transactionDate, feesAmount, loanTransactionDTO.getFeePayments());
             } else {
-                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                         AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId);
                 if (accountMap.containsKey(account)) {
-                    BigDecimal amount = accountMap.get(account).add(feesAmount);
+                    final BigDecimal amount = accountMap.get(account).add(feesAmount);
                     accountMap.put(account, amount);
                 } else {
                     accountMap.put(account, feesAmount);
@@ -1505,23 +1572,23 @@
             }
         }
 
-        // handle penalties payment of writeOff
+        // handle penalties payment
         if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
             totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
             if (isIncomeFromFee) {
-                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                         AccrualAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), paymentTypeId);
                 if (accountMap.containsKey(account)) {
-                    BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
+                    final BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
                     accountMap.put(account, amount);
                 } else {
                     accountMap.put(account, penaltiesAmount);
                 }
             } else {
-                GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                         AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), paymentTypeId);
                 if (accountMap.containsKey(account)) {
-                    BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
+                    final BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
                     accountMap.put(account, amount);
                 } else {
                     accountMap.put(account, penaltiesAmount);
@@ -1537,10 +1604,10 @@
 
         if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
             totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
-            GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, AccrualAccountsForLoan.OVERPAYMENT.getValue(),
-                    paymentTypeId);
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId);
             if (accountMap.containsKey(account)) {
-                BigDecimal amount = accountMap.get(account).add(overPaymentAmount);
+                final BigDecimal amount = accountMap.get(account).add(overPaymentAmount);
                 accountMap.put(account, amount);
             } else {
                 accountMap.put(account, overPaymentAmount);
@@ -1559,31 +1626,26 @@
         }
 
         /**
-         * Single DEBIT transaction for write-offs or Repayments
+         * Single DEBIT transaction for Repayments
          ***/
         if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
-            if (writeOff) {
-                this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(),
+            if (loanTransactionDTO.isLoanToLoanTransfer()) {
+                this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(), loanProductId,
+                        paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
+            } else if (loanTransactionDTO.isAccountTransfer()) {
+                this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
                         loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
             } else {
-                if (loanTransactionDTO.isLoanToLoanTransfer()) {
-                    this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.ASSET_TRANSFER.getValue(),
-                            loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
-                } else if (loanTransactionDTO.isAccountTransfer()) {
-                    this.helper.createDebitJournalEntryForLoan(office, currencyCode, FinancialActivity.LIABILITY_TRANSFER.getValue(),
-                            loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
-                } else {
-                    if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
-                        // create debit entries
-                        for (Map.Entry<Integer, BigDecimal> debitEntry : debitAccountMapForGoodwillCredit.entrySet()) {
-                            this.helper.createDebitJournalEntryForLoan(office, currencyCode, debitEntry.getKey().intValue(), loanProductId,
-                                    paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue());
-                        }
-
-                    } else {
-                        this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
-                                loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
+                if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                    // create debit entries
+                    for (Map.Entry<Integer, BigDecimal> debitEntry : debitAccountMapForGoodwillCredit.entrySet()) {
+                        this.helper.createDebitJournalEntryForLoan(office, currencyCode, debitEntry.getKey().intValue(), loanProductId,
+                                paymentTypeId, loanId, transactionId, transactionDate, debitEntry.getValue());
                     }
+
+                } else {
+                    this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
+                            loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
                 }
             }
         }
@@ -1593,12 +1655,108 @@
          * repayment above
          ***/
         if (MathUtil.isGreaterThanZero(totalDebitAmount) && loanTransactionDTO.getTransactionType().isChargeRefund()) {
-            Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
+            final Integer incomeAccount = this.helper.getValueForFeeOrPenaltyIncomeAccount(loanTransactionDTO.getChargeRefundChargeType());
             this.helper.createJournalEntriesForLoan(office, currencyCode, incomeAccount, AccrualAccountsForLoan.FUND_SOURCE.getValue(),
                     loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
         }
     }
 
+    private void createJournalEntriesForLoanWriteOffs(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
+            final Office office) {
+        // loan properties
+        final Long loanProductId = loanDTO.getLoanProductId();
+        final Long loanId = loanDTO.getLoanId();
+        final String currencyCode = loanDTO.getCurrencyCode();
+
+        // transaction properties
+        final String transactionId = loanTransactionDTO.getTransactionId();
+        final LocalDate transactionDate = loanTransactionDTO.getTransactionDate();
+        final BigDecimal principalAmount = loanTransactionDTO.getPrincipal();
+        final BigDecimal interestAmount = loanTransactionDTO.getInterest();
+        final BigDecimal feesAmount = loanTransactionDTO.getFees();
+        final BigDecimal penaltiesAmount = loanTransactionDTO.getPenalties();
+        final BigDecimal overPaymentAmount = loanTransactionDTO.getOverPayment();
+        final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
+
+        BigDecimal totalDebitAmount = new BigDecimal(0);
+
+        final Map<GLAccount, BigDecimal> accountMap = new LinkedHashMap<>();
+
+        // handle principal payment of writeOff
+        if (MathUtil.isGreaterThanZero(principalAmount)) {
+            totalDebitAmount = totalDebitAmount.add(principalAmount);
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), paymentTypeId);
+            accountMap.put(account, principalAmount);
+        }
+
+        // handle interest payment of writeOff
+        if (MathUtil.isGreaterThanZero(interestAmount)) {
+            totalDebitAmount = totalDebitAmount.add(interestAmount);
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.INTEREST_RECEIVABLE.getValue(), paymentTypeId);
+            if (accountMap.containsKey(account)) {
+                final BigDecimal amount = accountMap.get(account).add(interestAmount);
+                accountMap.put(account, amount);
+            } else {
+                accountMap.put(account, interestAmount);
+            }
+        }
+
+        // handle fees payment of writeOff
+        if (MathUtil.isGreaterThanZero(feesAmount)) {
+            totalDebitAmount = totalDebitAmount.add(feesAmount);
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(), paymentTypeId);
+            if (accountMap.containsKey(account)) {
+                final BigDecimal amount = accountMap.get(account).add(feesAmount);
+                accountMap.put(account, amount);
+            } else {
+                accountMap.put(account, feesAmount);
+            }
+        }
+
+        // handle penalties payment of writeOff
+        if (MathUtil.isGreaterThanZero(penaltiesAmount)) {
+            totalDebitAmount = totalDebitAmount.add(penaltiesAmount);
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(), paymentTypeId);
+            if (accountMap.containsKey(account)) {
+                final BigDecimal amount = accountMap.get(account).add(penaltiesAmount);
+                accountMap.put(account, amount);
+            } else {
+                accountMap.put(account, penaltiesAmount);
+            }
+        }
+
+        if (MathUtil.isGreaterThanZero(overPaymentAmount)) {
+            totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
+            final GLAccount account = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
+                    AccrualAccountsForLoan.OVERPAYMENT.getValue(), paymentTypeId);
+            if (accountMap.containsKey(account)) {
+                final BigDecimal amount = accountMap.get(account).add(overPaymentAmount);
+                accountMap.put(account, amount);
+            } else {
+                accountMap.put(account, overPaymentAmount);
+            }
+        }
+
+        for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
+            if (MathUtil.isGreaterThanZero(entry.getValue())) {
+                this.helper.createCreditJournalEntryForLoan(office, currencyCode, loanId, transactionId, transactionDate, entry.getValue(),
+                        entry.getKey());
+            }
+        }
+
+        /**
+         * Single DEBIT transaction for write-offs
+         ***/
+        if (MathUtil.isGreaterThanZero(totalDebitAmount)) {
+            this.helper.createDebitJournalEntryForLoan(office, currencyCode, AccrualAccountsForLoan.LOSSES_WRITTEN_OFF.getValue(),
+                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, totalDebitAmount);
+        }
+    }
+
     private void populateDebitAccountEntry(Long loanProductId, BigDecimal transactionPartAmount, Integer debitAccountType,
             Map<Integer, BigDecimal> accountMapForDebit, Long paymentTypeId) {
         Integer accountDebit = returnExistingDebitAccountInMapMatchingGLAccount(loanProductId, paymentTypeId, debitAccountType,