FINERACT-2042: chargeback with chargeoff
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 d92657b..51bab99 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
@@ -451,33 +451,54 @@
         }
 
         if (principalCredited.compareTo(principalPaid) > 0) {
-            helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
-                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalCredited.subtract(principalPaid),
-                    isReversal);
+            helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, getPrincipalAccount(loanDTO), loanProductId,
+                    paymentTypeId, loanId, transactionId, transactionDate, principalCredited.subtract(principalPaid), isReversal);
         } else if (principalCredited.compareTo(principalPaid) < 0) {
-            helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(),
-                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, principalPaid.subtract(principalCredited),
-                    isReversal);
+            helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, getPrincipalAccount(loanDTO), loanProductId,
+                    paymentTypeId, loanId, transactionId, transactionDate, principalPaid.subtract(principalCredited), isReversal);
         }
 
         if (feeCredited.compareTo(feePaid) > 0) {
-            helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
-                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, feeCredited.subtract(feePaid), isReversal);
+            helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, getFeeAccount(loanDTO), loanProductId, paymentTypeId,
+                    loanId, transactionId, transactionDate, feeCredited.subtract(feePaid), isReversal);
         } else if (feeCredited.compareTo(feePaid) < 0) {
-            helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.FEES_RECEIVABLE.getValue(),
-                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, feePaid.subtract(feeCredited), isReversal);
+            helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, getFeeAccount(loanDTO), loanProductId, paymentTypeId,
+                    loanId, transactionId, transactionDate, feePaid.subtract(feeCredited), isReversal);
         }
 
         if (penaltyCredited.compareTo(penaltyPaid) > 0) {
-            helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
-                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, penaltyCredited.subtract(penaltyPaid),
-                    isReversal);
+            helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, getPenaltyAccount(loanDTO), loanProductId, paymentTypeId,
+                    loanId, transactionId, transactionDate, penaltyCredited.subtract(penaltyPaid), isReversal);
         } else if (penaltyCredited.compareTo(penaltyPaid) < 0) {
-            helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue(),
-                    loanProductId, paymentTypeId, loanId, transactionId, transactionDate, penaltyPaid.subtract(penaltyCredited),
-                    isReversal);
+            helper.createCreditJournalEntryOrReversalForLoan(office, currencyCode, getPenaltyAccount(loanDTO), loanProductId, paymentTypeId,
+                    loanId, transactionId, transactionDate, penaltyPaid.subtract(penaltyCredited), isReversal);
         }
+    }
 
+    private Integer getFeeAccount(LoanDTO loanDTO) {
+        Integer account = AccrualAccountsForLoan.FEES_RECEIVABLE.getValue();
+        if (loanDTO.isMarkedAsChargeOff()) {
+            account = AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue();
+        }
+        return account;
+    }
+
+    private Integer getPenaltyAccount(LoanDTO loanDTO) {
+        Integer account = AccrualAccountsForLoan.PENALTIES_RECEIVABLE.getValue();
+        if (loanDTO.isMarkedAsChargeOff()) {
+            account = AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue();
+        }
+        return account;
+    }
+
+    private Integer getPrincipalAccount(LoanDTO loanDTO) {
+        if (loanDTO.isMarkedAsFraud() && loanDTO.isMarkedAsChargeOff()) {
+            return AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue();
+        } else if (!loanDTO.isMarkedAsFraud() && loanDTO.isMarkedAsChargeOff()) {
+            return AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue();
+        } else {
+            return AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue();
+        }
     }
 
     /**
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 7903ca4..a45d07e 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
@@ -23,6 +23,7 @@
 import static org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE;
 import static org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder.DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -68,6 +69,7 @@
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
 import org.apache.fineract.client.models.PostLoansRequest;
 import org.apache.fineract.client.models.PostLoansResponse;
+import org.apache.fineract.client.models.PutLoansLoanIdResponse;
 import org.apache.fineract.client.util.CallFailedRuntimeException;
 import org.apache.fineract.integrationtests.common.BatchHelper;
 import org.apache.fineract.integrationtests.common.BusinessDateHelper;
@@ -85,6 +87,7 @@
 import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
 import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.integrationtests.common.system.CodeHelper;
 import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
 import org.apache.fineract.integrationtests.useradministration.users.UserHelper;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleProcessingType;
@@ -140,11 +143,13 @@
     protected final Account feeIncomeAccount = accountHelper.createIncomeAccount("feeIncome");
     protected final Account penaltyIncomeAccount = accountHelper.createIncomeAccount("penaltyIncome");
     protected final Account feeChargeOffAccount = accountHelper.createIncomeAccount("feeChargeOff");
+    protected final Account penaltyChargeOffAccount = accountHelper.createIncomeAccount("penaltyChargeOff");
+
     protected final Account recoveriesAccount = accountHelper.createIncomeAccount("recoveries");
     protected final Account interestIncomeChargeOffAccount = accountHelper.createIncomeAccount("interestIncomeChargeOff");
     // expense
-    protected final Account creditLossBadDebtAccount = accountHelper.createExpenseAccount();
-    protected final Account creditLossBadDebtFraudAccount = accountHelper.createExpenseAccount();
+    protected final Account chargeOffExpenseAccount = accountHelper.createExpenseAccount("chargeOff");
+    protected final Account chargeOffFraudExpenseAccount = accountHelper.createExpenseAccount("chargeOffFraud");
     protected final Account writtenOffAccount = accountHelper.createExpenseAccount();
     protected final Account goodwillExpenseAccount = accountHelper.createExpenseAccount();
 
@@ -233,9 +238,10 @@
                 .incomeFromGoodwillCreditPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue())//
                 .incomeFromChargeOffInterestAccountId(interestIncomeChargeOffAccount.getAccountID().longValue())//
                 .incomeFromChargeOffFeesAccountId(feeChargeOffAccount.getAccountID().longValue())//
-                .chargeOffExpenseAccountId(creditLossBadDebtAccount.getAccountID().longValue())//
-                .chargeOffFraudExpenseAccountId(creditLossBadDebtFraudAccount.getAccountID().longValue())//
-                .incomeFromChargeOffPenaltyAccountId(feeChargeOffAccount.getAccountID().longValue()).dateFormat(DATETIME_PATTERN)//
+                .incomeFromChargeOffPenaltyAccountId(penaltyChargeOffAccount.getAccountID().longValue())//
+                .chargeOffExpenseAccountId(chargeOffExpenseAccount.getAccountID().longValue())//
+                .chargeOffFraudExpenseAccountId(chargeOffFraudExpenseAccount.getAccountID().longValue())//
+                .dateFormat(DATETIME_PATTERN)//
                 .locale("en_GB")//
                 .disallowExpectedDisbursements(true)//
                 .allowApprovedDisbursedAmountsOverApplied(true)//
@@ -398,7 +404,7 @@
 
     protected void verifyLastClosedBusinessDate(Long loanId, String lastClosedBusinessDate) {
         GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
-        Assertions.assertNotNull(loanDetails.getLastClosedBusinessDate());
+        assertNotNull(loanDetails.getLastClosedBusinessDate());
         Assertions.assertEquals(lastClosedBusinessDate, loanDetails.getLastClosedBusinessDate().format(dateTimeFormatter));
     }
 
@@ -438,10 +444,10 @@
     protected Long addCharge(Long loanId, boolean isPenalty, double amount, String dueDate) {
         Integer chargeId = ChargesHelper.createCharges(requestSpec, responseSpec,
                 ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, String.valueOf(amount), isPenalty));
-        Assertions.assertNotNull(chargeId);
+        assertNotNull(chargeId);
         Integer loanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId.intValue(),
                 LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(chargeId), dueDate, String.valueOf(amount)));
-        Assertions.assertNotNull(loanChargeId);
+        assertNotNull(loanChargeId);
         return loanChargeId.longValue();
     }
 
@@ -449,8 +455,8 @@
         GetLoansLoanIdResponse loanResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATETIME_PATTERN);
 
-        Assertions.assertNotNull(loanResponse.getRepaymentSchedule());
-        Assertions.assertNotNull(loanResponse.getRepaymentSchedule().getPeriods());
+        assertNotNull(loanResponse.getRepaymentSchedule());
+        assertNotNull(loanResponse.getRepaymentSchedule().getPeriods());
         Assertions.assertEquals(installments.length, loanResponse.getRepaymentSchedule().getPeriods().size(),
                 "Expected installments are not matching with the installments configured on the loan");
 
@@ -621,6 +627,24 @@
         return response.getResourceId();
     }
 
+    protected Long chargeOffLoan(Long loanId, String date) {
+        String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6) + Utils.randomStringGenerator("is", 5);
+        Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
+        String transactionExternalId = UUID.randomUUID().toString();
+
+        PostLoansLoanIdTransactionsResponse chargeOffTransaction = this.loanTransactionHelper.chargeOffLoan((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().transactionDate(date).locale("en").dateFormat("dd MMMM yyyy")
+                        .externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
+        return chargeOffTransaction.getResourceId();
+    }
+
+    protected void changeLoanFraudState(Long loanId, boolean fraudState) {
+        String payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", fraudState ? "true" : "false");
+        PutLoansLoanIdResponse response = loanTransactionHelper.modifyLoanCommand(Math.toIntExact(loanId), "markAsFraud", payload,
+                responseSpec);
+        assertNotNull(response);
+    }
+
     protected Long addChargebackForLoan(Long loanId, Long transactionId, Double amount) {
         PostLoansLoanIdTransactionsResponse response = loanTransactionHelper.chargebackLoanTransaction(loanId, transactionId,
                 new PostLoansLoanIdTransactionsTransactionIdRequest().locale("en").transactionAmount(amount).paymentTypeId(1L));
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 ccb3e1a..15986d9 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
@@ -1460,6 +1460,321 @@
         });
     }
 
+    @Test
+    public void testAccountingChargebackOnChargeOffWithPrincipal() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            // Create Loan Product
+            Long loanProductId = createLoanProduct(//
+                    createDefaultPaymentAllocation(), //
+                    chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
+            );
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0, null, "01 January 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
+            );
+
+            // Repayment #1
+            updateBusinessDate("01 February 2023");
+            Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
+
+            // Repayment #2
+            updateBusinessDate("01 March 2023");
+            Long repaymentTransaction2 = addRepaymentForLoan(loanId, 250.0, "01 March 2023");
+
+            // Charge-Off
+            updateBusinessDate("15 March 2023");
+            Long chargeOff = chargeOffLoan(loanId, "15 March 2023");
+
+            // Chargeback 250
+            updateBusinessDate("30 March 2023");
+            Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 250.0);
+
+            verifyTransactions(loanId, //
+                    transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Charge-off", "15 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Chargeback", "30 March 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
+            );
+
+            // Verify GL entries
+            verifyTRJournalEntries(repaymentTransaction1, //
+                    debit(fundSource, 250), //
+                    credit(loansReceivableAccount, 250)//
+            );
+
+            verifyTRJournalEntries(repaymentTransaction2, //
+                    debit(fundSource, 250), //
+                    credit(loansReceivableAccount, 250) //
+            );
+
+            verifyTRJournalEntries(chargeOff, //
+                    debit(chargeOffExpenseAccount, 250), //
+                    credit(loansReceivableAccount, 250)//
+            );
+
+            verifyTRJournalEntries(chargeback, //
+                    debit(chargeOffExpenseAccount, 250), //
+                    credit(fundSource, 250) //
+            );
+        });
+    }
+
+    @Test
+    public void testAccountingChargebackOnChargeOffFraudWithPrincipal() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            // Create Loan Product
+            Long loanProductId = createLoanProduct(//
+                    createDefaultPaymentAllocation(), //
+                    chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
+            );
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0, null, "01 January 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
+            );
+
+            // Repayment #1
+            updateBusinessDate("01 February 2023");
+            Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
+
+            // Repayment #2
+            updateBusinessDate("01 March 2023");
+            Long repaymentTransaction2 = addRepaymentForLoan(loanId, 250.0, "01 March 2023");
+
+            // Charge-Off
+            updateBusinessDate("15 March 2023");
+            Long chargeOff = chargeOffLoan(loanId, "15 March 2023");
+            changeLoanFraudState(loanId, true);
+
+            // Chargeback 250
+            updateBusinessDate("30 March 2023");
+            Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 250.0);
+
+            verifyTransactions(loanId, //
+                    transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Charge-off", "15 March 2023", 0.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Chargeback", "30 March 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0) //
+            );
+
+            // Verify GL entries
+            verifyTRJournalEntries(repaymentTransaction1, //
+                    debit(fundSource, 250), //
+                    credit(loansReceivableAccount, 250)//
+            );
+
+            verifyTRJournalEntries(repaymentTransaction2, //
+                    debit(fundSource, 250), //
+                    credit(loansReceivableAccount, 250) //
+            );
+
+            verifyTRJournalEntries(chargeOff, //
+                    debit(chargeOffExpenseAccount, 250), //
+                    credit(loansReceivableAccount, 250)//
+            );
+
+            verifyTRJournalEntries(chargeback, //
+                    debit(chargeOffFraudExpenseAccount, 250), //
+                    credit(fundSource, 250) //
+            );
+        });
+    }
+
+    @Test
+    public void testAccountingChargebackOnChargeOffWithFees() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            // Create Loan Product
+            Long loanProductId = createLoanProduct(//
+                    createDefaultPaymentAllocation(), //
+                    chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
+            );
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0, null, "01 January 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
+            );
+
+            // Repayment #1
+            updateBusinessDate("01 February 2023");
+            Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
+
+            // Add fee 30
+            updateBusinessDate("01 March 2023");
+            addCharge(loanId, false, 30, "01 March 2023");
+
+            // Repayment #2
+            Long repaymentTransaction2 = addRepaymentForLoan(loanId, 280.0, "01 March 2023");
+
+            // Run periodic accrual
+            schedulerJobHelper.executeAndAwaitJob("Add Accrual Transactions");
+
+            // Charge-Off
+            updateBusinessDate("15 March 2023");
+            addCharge(loanId, false, 20, "15 March 2023");
+            Long chargeOff = chargeOffLoan(loanId, "15 March 2023");
+
+            // Chargeback 250
+            updateBusinessDate("30 March 2023");
+            Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 280.0);
+
+            verifyTransactions(loanId, //
+                    transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(30.0, "Accrual", "01 March 2023", 0.0, 0.0, 0.0, 30.0, 0.0, 0.0, 0.0), //
+                    transaction(280.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 30.0, 0.0, 0.0, 0.0), //
+                    transaction(270.0, "Charge-off", "15 March 2023", 0.0, 250.0, 0.0, 20.0, 0.0, 0.0, 0.0), //
+                    transaction(280.0, "Chargeback", "30 March 2023", 500.0, 250.0, 0.0, 30.0, 0.0, 0.0, 0.0) //
+            );
+
+            // Verify GL entries
+            verifyTRJournalEntries(repaymentTransaction1, //
+                    debit(fundSource, 250), //
+                    credit(loansReceivableAccount, 250)//
+            );
+
+            verifyTRJournalEntries(repaymentTransaction2, //
+                    debit(fundSource, 280), //
+                    credit(loansReceivableAccount, 250), //
+                    credit(feeReceivableAccount, 30) //
+            );
+
+            verifyTRJournalEntries(getTransactionId(loanId, "Accrual", "01 March 2023"), //
+                    debit(feeReceivableAccount, 30), //
+                    credit(feeIncomeAccount, 30) //
+            );
+
+            verifyTRJournalEntries(chargeOff, //
+                    debit(chargeOffExpenseAccount, 250), //
+                    credit(loansReceivableAccount, 250), //
+                    credit(feeReceivableAccount, 20), //
+                    debit(feeChargeOffAccount, 20) //
+            );
+
+            verifyTRJournalEntries(chargeback, //
+                    credit(fundSource, 280), //
+                    debit(chargeOffExpenseAccount, 250), //
+                    debit(feeChargeOffAccount, 30) //
+            );
+        });
+    }
+
+    @Test
+    public void testAccountingChargebackOnChargeOffWithPenalties() {
+        runAt("01 January 2023", () -> {
+            // Create Client
+            Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+            // Create Loan Product
+            Long loanProductId = createLoanProduct(//
+                    createDefaultPaymentAllocation(), //
+                    chargebackAllocation("PENALTY", "FEE", "INTEREST", "PRINCIPAL")//
+            );
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, loanProductId, 3);
+
+            // Disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(750), "01 January 2023");
+
+            verifyRepaymentSchedule(loanId, //
+                    installment(0, null, "01 January 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 February 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 March 2023"), //
+                    installment(250.0, 0, 0, 0, 250.0, false, "01 April 2023") //
+            );
+
+            // Repayment #1
+            updateBusinessDate("01 February 2023");
+            Long repaymentTransaction1 = addRepaymentForLoan(loanId, 250.0, "01 February 2023");
+
+            // Add fee 30
+            updateBusinessDate("01 March 2023");
+            addCharge(loanId, true, 30, "01 March 2023");
+
+            // Repayment #2
+            Long repaymentTransaction2 = addRepaymentForLoan(loanId, 280.0, "01 March 2023");
+
+            // Run periodic accrual
+            schedulerJobHelper.executeAndAwaitJob("Add Accrual Transactions");
+
+            // Charge-Off
+            updateBusinessDate("15 March 2023");
+            addCharge(loanId, true, 20, "15 March 2023");
+            Long chargeOff = chargeOffLoan(loanId, "15 March 2023");
+
+            // Chargeback 250
+            updateBusinessDate("30 March 2023");
+            Long chargeback = addChargebackForLoan(loanId, repaymentTransaction2, 280.0);
+
+            verifyTransactions(loanId, //
+                    transaction(750.0, "Disbursement", "01 January 2023", 750.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(250.0, "Repayment", "01 February 2023", 500.0, 250.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
+                    transaction(30.0, "Accrual", "01 March 2023", 0.0, 0.0, 0.0, 0.0, 30.0, 0.0, 0.0), //
+                    transaction(280.0, "Repayment", "01 March 2023", 250.0, 250.0, 0.0, 0.0, 30.0, 0.0, 0.0), //
+                    transaction(270.0, "Charge-off", "15 March 2023", 0.0, 250.0, 0.0, 0.0, 20.0, 0.0, 0.0), //
+                    transaction(280.0, "Chargeback", "30 March 2023", 500.0, 250.0, 0.0, 0.0, 30.0, 0.0, 0.0) //
+            );
+
+            // Verify GL entries
+            verifyTRJournalEntries(repaymentTransaction1, //
+                    debit(fundSource, 250), //
+                    credit(loansReceivableAccount, 250)//
+            );
+
+            verifyTRJournalEntries(repaymentTransaction2, //
+                    debit(fundSource, 280), //
+                    credit(loansReceivableAccount, 250), //
+                    credit(penaltyReceivableAccount, 30) //
+            );
+
+            verifyTRJournalEntries(getTransactionId(loanId, "Accrual", "01 March 2023"), //
+                    debit(penaltyReceivableAccount, 30), //
+                    credit(penaltyIncomeAccount, 30) //
+            );
+
+            verifyTRJournalEntries(chargeOff, //
+                    debit(chargeOffExpenseAccount, 250), //
+                    credit(loansReceivableAccount, 250), //
+                    credit(penaltyReceivableAccount, 20), //
+                    debit(penaltyChargeOffAccount, 20) //
+            );
+
+            verifyTRJournalEntries(chargeback, //
+                    credit(fundSource, 280), //
+                    debit(chargeOffExpenseAccount, 250), //
+                    debit(penaltyChargeOffAccount, 30) //
+            );
+        });
+    }
+
     private void verifyLoanSummaryAmounts(Long loanId, double creditedPrincipal, double creditedFee, double creditedPenalty,
             double totalOutstanding) {
         GetLoansLoanIdResponse loanResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
@@ -1471,7 +1786,6 @@
         Assertions.assertEquals(totalOutstanding, summary.getTotalOutstanding());
     }
 
-    @Nullable
     private Long applyAndApproveLoan(Long clientId, Long loanProductId, int numberOfRepayments) {
         PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2023", 1250.0, numberOfRepayments)//
                 .repaymentEvery(1)//
@@ -1484,7 +1798,8 @@
 
         PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
                 approveLoanRequest(1250.0, "01 January 2023"));
-
+        Assertions.assertNotNull(approvedLoanResult);
+        Assertions.assertNotNull(approvedLoanResult.getLoanId());
         return approvedLoanResult.getLoanId();
     }