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

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new ceb22456c  FINERACT-1596-Accounting-treatment-changes-goodwill-credit
ceb22456c is described below

commit ceb22456ca949e1d933cdf88b8ceb1fb97abb7b6
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Thu Mar 9 18:22:54 2023 +0530

     FINERACT-1596-Accounting-treatment-changes-goodwill-credit
---
 .../accounting/common/AccountingConstants.java     |  20 +-
 .../AccrualBasedAccountingProcessorForLoan.java    |  66 ++++-
 .../CashBasedAccountingProcessorForLoan.java       |  61 ++++-
 .../LoanProductToGLAccountMappingHelper.java       |  18 ++
 .../service/ProductToGLAccountMappingHelper.java   |   4 +
 ...tToGLAccountMappingReadPlatformServiceImpl.java |  17 ++
 ...ToGLAccountMappingWritePlatformServiceImpl.java |  18 ++
 .../serialization/LoanProductDataValidator.java    |  36 ++-
 ...lanceRefundandRepaymentTypeIntegrationTest.java |   6 +-
 .../LoanChargeOffAccountingTest.java               | 281 ++++++++++++++++++++-
 .../common/loans/LoanProductTestBuilder.java       |   6 +
 11 files changed, 502 insertions(+), 31 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
index dcc168565..ca0786ad2 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
@@ -38,8 +38,9 @@ public final class AccountingConstants {
 
         FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), 
INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(
                 6), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), 
INCOME_FROM_RECOVERY(12), GOODWILL_CREDIT(13), INCOME_FROM_CHARGE_OFF_INTEREST(
-                        14), INCOME_FROM_CHARGE_OFF_FEES(
-                                15), CHARGE_OFF_EXPENSE(16), 
CHARGE_OFF_FRAUD_EXPENSE(17), INCOME_FROM_CHARGE_OFF_PENALTY(18);
+                        14), INCOME_FROM_CHARGE_OFF_FEES(15), 
CHARGE_OFF_EXPENSE(16), CHARGE_OFF_FRAUD_EXPENSE(
+                                17), INCOME_FROM_CHARGE_OFF_PENALTY(18), 
INCOME_FROM_GOODWILL_CREDIT_INTEREST(
+                                        19), 
INCOME_FROM_GOODWILL_CREDIT_FEES(20), INCOME_FROM_GOODWILL_CREDIT_PENALTY(21);
 
         private final Integer value;
 
@@ -78,8 +79,9 @@ public final class AccountingConstants {
         FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), 
INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), //
         LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(7), FEES_RECEIVABLE(8), 
PENALTIES_RECEIVABLE(9), //
         TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12), 
GOODWILL_CREDIT(13), INCOME_FROM_CHARGE_OFF_INTEREST(
-                14), INCOME_FROM_CHARGE_OFF_FEES(
-                        15), CHARGE_OFF_EXPENSE(16), 
CHARGE_OFF_FRAUD_EXPENSE(17), INCOME_FROM_CHARGE_OFF_PENALTY(18);
+                14), INCOME_FROM_CHARGE_OFF_FEES(15), CHARGE_OFF_EXPENSE(16), 
CHARGE_OFF_FRAUD_EXPENSE(17), INCOME_FROM_CHARGE_OFF_PENALTY(
+                        18), INCOME_FROM_GOODWILL_CREDIT_INTEREST(
+                                19), INCOME_FROM_GOODWILL_CREDIT_FEES(20), 
INCOME_FROM_GOODWILL_CREDIT_PENALTY(21);
 
         private final Integer value;
 
@@ -134,7 +136,10 @@ public final class AccountingConstants {
                                                                                
                                                 
"incomeFromChargeOffFeesAccountId"), CHARGE_OFF_EXPENSE(
                                                                                
                                                         
"chargeOffExpenseAccountId"), CHARGE_OFF_FRAUD_EXPENSE(
                                                                                
                                                                 
"chargeOffFraudExpenseAccountId"), INCOME_FROM_CHARGE_OFF_PENALTY(
-                                                                               
                                                                         
"incomeFromChargeOffPenaltyAccountId");
+                                                                               
                                                                         
"incomeFromChargeOffPenaltyAccountId"), INCOME_FROM_GOODWILL_CREDIT_INTEREST(
+                                                                               
                                                                                
 "incomeFromGoodwillCreditInterestAccountId"), INCOME_FROM_GOODWILL_CREDIT_FEES(
+                                                                               
                                                                                
         "incomeFromGoodwillCreditFeesAccountId"), 
INCOME_FROM_GOODWILL_CREDIT_PENALTY(
+                                                                               
                                                                                
                 "incomeFromGoodwillCreditPenaltyAccountId");
 
         private final String value;
 
@@ -168,7 +173,10 @@ public final class AccountingConstants {
                                                                                
                 "incomeFromChargeOffFeesAccount"), CHARGE_OFF_EXPENSE(
                                                                                
                         "chargeOffExpenseAccount"), CHARGE_OFF_FRAUD_EXPENSE(
                                                                                
                                 "chargeOffFraudExpenseAccount"), 
INCOME_FROM_CHARGE_OFF_PENALTY(
-                                                                               
                                         "incomeFromChargeOffPenaltyAccount");
+                                                                               
                                         "incomeFromChargeOffPenaltyAccount"), 
INCOME_FROM_GOODWILL_CREDIT_INTEREST(
+                                                                               
                                                 
"incomeFromGoodwillCreditInterestAccount"), INCOME_FROM_GOODWILL_CREDIT_FEES(
+                                                                               
                                                         
"incomeFromGoodwillCreditFeesAccount"), INCOME_FROM_GOODWILL_CREDIT_PENALTY(
+                                                                               
                                                                 
"incomeFromGoodwillCreditPenaltyAccount");
 
         private final String value;
 
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 225047ee5..690672d1d 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
@@ -186,7 +186,9 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
         } else {
             accountMapForCredit.put(accountCredit, transactionPartAmount);
         }
-        Integer accountDebit = debitAccountType;
+        Integer accountDebit = 
returnExistingDebitAccountInMapMatchingGLAccount(loanProductId, paymentTypeId, 
debitAccountType,
+                accountMapForDebit);
+
         if (accountMapForDebit.containsKey(accountDebit)) {
             BigDecimal amount = 
accountMapForDebit.get(accountDebit).add(transactionPartAmount);
             accountMapForDebit.put(accountDebit, amount);
@@ -595,7 +597,7 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId,
                         AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
accountMapForCredit, accountMapForDebit);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId,
@@ -624,7 +626,7 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), 
accountMapForCredit, accountMapForDebit);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
@@ -667,8 +669,8 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId,
-                        
AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(), 
AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        AccrualAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
accountMapForCredit, accountMapForDebit);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId,
@@ -780,6 +782,7 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
         BigDecimal totalDebitAmount = new BigDecimal(0);
 
         Map<GLAccount, BigDecimal> accountMap = new LinkedHashMap<>();
+        Map<Integer, BigDecimal> debitAccountMapForGoodwillCredit = new 
LinkedHashMap<>();
 
         // handle principal payment or writeOff (and reversals)
         if (principalAmount != null && 
principalAmount.compareTo(BigDecimal.ZERO) > 0) {
@@ -787,6 +790,10 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
             GLAccount account = 
this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                     AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), 
paymentTypeId);
             accountMap.put(account, principalAmount);
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, principalAmount, 
AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(),
+                        debitAccountMapForGoodwillCredit, paymentTypeId);
+            }
         }
 
         // handle interest payment of writeOff (and reversals)
@@ -800,6 +807,11 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
             } else {
                 accountMap.put(account, interestAmount);
             }
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, interestAmount,
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
debitAccountMapForGoodwillCredit,
+                        paymentTypeId);
+            }
         }
 
         // handle fees payment of writeOff (and reversals)
@@ -821,6 +833,10 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
                     accountMap.put(account, feesAmount);
                 }
             }
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, feesAmount, 
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+                        debitAccountMapForGoodwillCredit, paymentTypeId);
+            }
         }
 
         // handle penalties payment of writeOff (and reversals)
@@ -845,6 +861,12 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
                     accountMap.put(account, penaltiesAmount);
                 }
             }
+
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, penaltiesAmount,
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
debitAccountMapForGoodwillCredit,
+                        paymentTypeId);
+            }
         }
 
         if (overPaymentAmount != null && 
overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
@@ -857,6 +879,10 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
             } else {
                 accountMap.put(account, overPaymentAmount);
             }
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, overPaymentAmount, 
AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(),
+                        debitAccountMapForGoodwillCredit, paymentTypeId);
+            }
         }
 
         for (Map.Entry<GLAccount, BigDecimal> entry : accountMap.entrySet()) {
@@ -882,9 +908,12 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
                             transactionDate, totalDebitAmount, isReversal);
                 } else {
                     if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
-                        
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
-                                
AccrualAccountsForLoan.GOODWILL_CREDIT.getValue(), loanProductId, 
paymentTypeId, loanId, transactionId,
-                                transactionDate, totalDebitAmount, isReversal);
+                        // create debit entries
+                        for (Map.Entry<Integer, BigDecimal> debitEntry : 
debitAccountMapForGoodwillCredit.entrySet()) {
+                            
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, 
debitEntry.getKey().intValue(),
+                                    loanProductId, paymentTypeId, loanId, 
transactionId, transactionDate, debitEntry.getValue(),
+                                    isReversal);
+                        }
 
                     } else {
                         
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
@@ -907,6 +936,27 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
         }
     }
 
+    private void populateDebitAccountEntry(Long loanProductId, BigDecimal 
transactionPartAmount, Integer debitAccountType,
+            Map<Integer, BigDecimal> accountMapForDebit, Long paymentTypeId) {
+        Integer accountDebit = 
returnExistingDebitAccountInMapMatchingGLAccount(loanProductId, paymentTypeId, 
debitAccountType,
+                accountMapForDebit);
+        if (accountMapForDebit.containsKey(accountDebit)) {
+            BigDecimal amount = 
accountMapForDebit.get(accountDebit).add(transactionPartAmount);
+            accountMapForDebit.put(accountDebit, amount);
+        } else {
+            accountMapForDebit.put(accountDebit, transactionPartAmount);
+        }
+    }
+
+    private Integer returnExistingDebitAccountInMapMatchingGLAccount(Long 
loanProductId, Long paymentTypeId, Integer accountType,
+            Map<Integer, BigDecimal> accountMap) {
+        GLAccount glAccount = 
this.helper.getLinkedGLAccountForLoanProduct(loanProductId, accountType, 
paymentTypeId);
+        Integer accountEntry = accountMap.entrySet().stream().filter(account 
-> this.helper
+                .getLinkedGLAccountForLoanProduct(loanProductId, 
account.getKey(), paymentTypeId).getGlCode().equals(glAccount.getGlCode()))
+                .map(Map.Entry::getKey).findFirst().orElse(accountType);
+        return accountEntry;
+    }
+
     /**
      * Create a single Debit to fund source and a single credit to "Income 
from Recovery"
      *
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
index f8bae9f62..6786c8a7f 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/CashBasedAccountingProcessorForLoan.java
@@ -195,7 +195,8 @@ public class CashBasedAccountingProcessorForLoan implements 
AccountingProcessorF
         } else {
             accountMapForCredit.put(accountCredit, transactionPartAmount);
         }
-        Integer accountDebit = debitAccountType;
+        Integer accountDebit = 
returnExistingDebitAccountInMapMatchingGLAccount(loanProductId, paymentTypeId, 
debitAccountType,
+                accountMapForDebit);
         if (accountMapForDebit.containsKey(accountDebit)) {
             BigDecimal amount = 
accountMapForDebit.get(accountDebit).add(transactionPartAmount);
             accountMapForDebit.put(accountDebit, amount);
@@ -204,6 +205,15 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
         }
     }
 
+    private Integer returnExistingDebitAccountInMapMatchingGLAccount(Long 
loanProductId, Long paymentTypeId, Integer accountType,
+            Map<Integer, BigDecimal> accountMap) {
+        GLAccount glAccount = 
this.helper.getLinkedGLAccountForLoanProduct(loanProductId, accountType, 
paymentTypeId);
+        Integer accountEntry = accountMap.entrySet().stream().filter(account 
-> this.helper
+                .getLinkedGLAccountForLoanProduct(loanProductId, 
account.getKey(), paymentTypeId).getGlCode().equals(glAccount.getGlCode()))
+                .map(Map.Entry::getKey).findFirst().orElse(accountType);
+        return accountEntry;
+    }
+
     private void createJournalEntriesForChargeAdjustment(LoanDTO loanDTO, 
LoanTransactionDTO loanTransactionDTO, Office office) {
         final boolean isMarkedAsChargeOff = loanDTO.isMarkedAsChargeOff();
         if (isMarkedAsChargeOff) {
@@ -609,7 +619,7 @@ public class CashBasedAccountingProcessorForLoan implements 
AccountingProcessorF
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
accountMapForCredit, accountMapForDebit);
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
                         CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
@@ -636,7 +646,7 @@ public class CashBasedAccountingProcessorForLoan implements 
AccountingProcessorF
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.GOODWILL_CREDIT.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), 
accountMapForCredit, accountMapForDebit);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
@@ -664,7 +674,7 @@ public class CashBasedAccountingProcessorForLoan implements 
AccountingProcessorF
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.GOODWILL_CREDIT.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
accountMapForCredit, accountMapForDebit);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
@@ -755,17 +765,27 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
         final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
 
         BigDecimal totalDebitAmount = new BigDecimal(0);
+        Map<Integer, BigDecimal> debitAccountMapForGoodwillCredit = new 
LinkedHashMap<>();
 
         if (principalAmount != null && 
principalAmount.compareTo(BigDecimal.ZERO) > 0) {
             totalDebitAmount = totalDebitAmount.add(principalAmount);
             this.helper.createCreditJournalEntryOrReversalForLoan(office, 
currencyCode, CashAccountsForLoan.LOAN_PORTFOLIO, loanProductId,
                     paymentTypeId, loanId, transactionId, transactionDate, 
principalAmount, isReversal);
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, principalAmount, 
CashAccountsForLoan.GOODWILL_CREDIT.getValue(),
+                        debitAccountMapForGoodwillCredit, paymentTypeId);
+            }
         }
 
         if (interestAmount != null && 
interestAmount.compareTo(BigDecimal.ZERO) > 0) {
             totalDebitAmount = totalDebitAmount.add(interestAmount);
             this.helper.createCreditJournalEntryOrReversalForLoan(office, 
currencyCode, CashAccountsForLoan.INTEREST_ON_LOANS,
                     loanProductId, paymentTypeId, loanId, transactionId, 
transactionDate, interestAmount, isReversal);
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, interestAmount,
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
debitAccountMapForGoodwillCredit,
+                        paymentTypeId);
+            }
         }
 
         if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
@@ -773,6 +793,10 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
             
this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, 
currencyCode,
                     CashAccountsForLoan.INCOME_FROM_FEES.getValue(), 
loanProductId, loanId, transactionId, transactionDate, feesAmount,
                     isReversal, loanTransactionDTO.getFeePayments());
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, feesAmount, 
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+                        debitAccountMapForGoodwillCredit, paymentTypeId);
+            }
         }
 
         if (penaltiesAmount != null && 
penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
@@ -780,12 +804,21 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
             
this.helper.createCreditJournalEntryOrReversalForLoanCharges(office, 
currencyCode,
                     CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(), 
loanProductId, loanId, transactionId, transactionDate,
                     penaltiesAmount, isReversal, 
loanTransactionDTO.getPenaltyPayments());
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, penaltiesAmount,
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
debitAccountMapForGoodwillCredit,
+                        paymentTypeId);
+            }
         }
 
         if (overPaymentAmount != null && 
overPaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
             totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
             this.helper.createCreditJournalEntryOrReversalForLoan(office, 
currencyCode, CashAccountsForLoan.OVERPAYMENT, loanProductId,
                     paymentTypeId, loanId, transactionId, transactionDate, 
overPaymentAmount, isReversal);
+            if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                populateDebitAccountEntry(loanProductId, overPaymentAmount, 
CashAccountsForLoan.GOODWILL_CREDIT.getValue(),
+                        debitAccountMapForGoodwillCredit, paymentTypeId);
+            }
         }
 
         /*** create a single debit entry (or reversal) for the entire amount 
**/
@@ -797,8 +830,12 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
                     loanProductId, paymentTypeId, loanId, transactionId, 
transactionDate, totalDebitAmount, isReversal);
         } else {
             if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
-                this.helper.createDebitJournalEntryOrReversalForLoan(office, 
currencyCode, CashAccountsForLoan.GOODWILL_CREDIT.getValue(),
-                        loanProductId, paymentTypeId, loanId, transactionId, 
transactionDate, totalDebitAmount, isReversal);
+
+                // create debit entries
+                for (Map.Entry<Integer, BigDecimal> debitEntry : 
debitAccountMapForGoodwillCredit.entrySet()) {
+                    
this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode, 
debitEntry.getKey().intValue(),
+                            loanProductId, paymentTypeId, loanId, 
transactionId, transactionDate, debitEntry.getValue(), isReversal);
+                }
 
             } else {
                 this.helper.createDebitJournalEntryOrReversalForLoan(office, 
currencyCode, CashAccountsForLoan.FUND_SOURCE.getValue(),
@@ -818,6 +855,18 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
         }
     }
 
+    private void populateDebitAccountEntry(Long loanProductId, BigDecimal 
transactionPartAmount, Integer debitAccountType,
+            Map<Integer, BigDecimal> accountMapForDebit, Long paymentTypeId) {
+        Integer accountDebit = 
returnExistingDebitAccountInMapMatchingGLAccount(loanProductId, paymentTypeId, 
debitAccountType,
+                accountMapForDebit);
+        if (accountMapForDebit.containsKey(accountDebit)) {
+            BigDecimal amount = 
accountMapForDebit.get(accountDebit).add(transactionPartAmount);
+            accountMapForDebit.put(accountDebit, amount);
+        } else {
+            accountMapForDebit.put(accountDebit, transactionPartAmount);
+        }
+    }
+
     /**
      * Create a single Debit to fund source and a single credit to "Income 
from Recovery"
      *
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
index fc2c49615..20cfce149 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/LoanProductToGLAccountMappingHelper.java
@@ -272,6 +272,15 @@ public class LoanProductToGLAccountMappingHelper extends 
ProductToGLAccountMappi
                 mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
                         loanProductId, 
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.toString(), changes);
+                mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+                        loanProductId, 
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.toString(), changes);
+                mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+                        loanProductId, 
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.toString(), changes);
+                mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+                        loanProductId, 
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.toString(), changes);
 
                 // expenses
                 mergeLoanToExpenseAccountMappingChanges(element, 
LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
@@ -330,6 +339,15 @@ public class LoanProductToGLAccountMappingHelper extends 
ProductToGLAccountMappi
                 mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
                         loanProductId, 
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
                         
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.toString(), changes);
+                mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+                        loanProductId, 
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.toString(), 
changes);
+                mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+                        loanProductId, 
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.toString(), changes);
+                mergeLoanToIncomeAccountMappingChanges(element, 
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+                        loanProductId, 
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.toString(), changes);
 
                 // expenses
                 mergeLoanToExpenseAccountMappingChanges(element, 
LoanProductAccountingParams.LOSSES_WRITTEN_OFF.getValue(), loanProductId,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
index a9acb2bbb..930b27369 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
@@ -89,6 +89,10 @@ public class ProductToGLAccountMappingHelper {
                 
optionalProductToGLAccountMappingEntries.add("chargeOffAccountId");
                 
optionalProductToGLAccountMappingEntries.add("chargeOffFraudAccountId");
                 
optionalProductToGLAccountMappingEntries.add("incomeFromChargeOffPenaltyAccountId");
+                
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditInterestAccountId");
+                
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditFeesAccountId");
+                
optionalProductToGLAccountMappingEntries.add("incomeFromGoodwillCreditPenaltyAccountId");
+
                 if 
(optionalProductToGLAccountMappingEntries.contains(paramName)) {
                     saveProductToAccountMapping(element, paramName, productId, 
accountTypeId, expectedAccountType, portfolioProductType);
                 } else {
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
index c34e27c87..fa9bb087c 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
@@ -148,7 +148,16 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
                     
accountMappingDetails.put(LoanProductAccountingDataParams.CHARGE_OFF_FRAUD_EXPENSE.getValue(),
 gLAccountData);
                 } else if 
(glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY)) {
                     
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
 gLAccountData);
+                } else if 
(glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST))
 {
+                    
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+                            gLAccountData);
+                } else if 
(glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES)) 
{
+                    
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
 gLAccountData);
+                } else if 
(glAccountForLoan.equals(CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY))
 {
+                    
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+                            gLAccountData);
                 }
+
             }
         } else if 
(AccountingRuleType.ACCRUAL_UPFRONT.getValue().equals(accountingType)
                 || 
AccountingRuleType.ACCRUAL_PERIODIC.getValue().equals(accountingType)) {
@@ -198,6 +207,14 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
                     
accountMappingDetails.put(LoanProductAccountingDataParams.CHARGE_OFF_FRAUD_EXPENSE.getValue(),
 gLAccountData);
                 } else if 
(glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY))
 {
                     
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
 gLAccountData);
+                } else if 
(glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST))
 {
+                    
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+                            gLAccountData);
+                } else if 
(glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES))
 {
+                    
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
 gLAccountData);
+                } else if 
(glAccountForLoan.equals(AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY))
 {
+                    
accountMappingDetails.put(LoanProductAccountingDataParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+                            gLAccountData);
                 }
             }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
index c09d8ec64..ad957c5d0 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingWritePlatformServiceImpl.java
@@ -95,6 +95,15 @@ public class 
ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
                 
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
                         
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
loanProductId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue());
+                
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+                        
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
loanProductId,
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue());
+                
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+                        
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), 
loanProductId,
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue());
+                
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+                        
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
loanProductId,
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue());
 
                 // expenses
                 
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
@@ -164,6 +173,15 @@ public class 
ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
                 
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
                         
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
loanProductId,
                         
AccrualAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue());
+                
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+                        
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
loanProductId,
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue());
+                
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+                        
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), 
loanProductId,
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue());
+                
this.loanProductToGLAccountMappingHelper.saveLoanToIncomeAccountMapping(element,
+                        
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
loanProductId,
+                        
AccrualAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue());
 
                 // expenses
                 
this.loanProductToGLAccountMappingHelper.saveLoanToExpenseAccountMapping(element,
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
index 0fe3ac997..8c44abb38 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java
@@ -123,7 +123,11 @@ public final class LoanProductDataValidator {
             LoanProductAccountingParams.CHARGE_OFF_FRAUD_EXPENSE.getValue(), 
LoanProductAccountingParams.CHARGE_OFF_EXPENSE.getValue(),
             LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_FEES.getValue(),
             
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(),
-            
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME,
+            
LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(),
+            
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
+            
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
+            
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
+            LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME,
             
LoanProductConstants.PRINCIPAL_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME,
             
LoanProductConstants.INTEREST_RATE_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME,
             
LoanProductConstants.NUMBER_OF_REPAYMENT_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME,
 LoanProductConstants.SHORT_NAME,
@@ -658,6 +662,21 @@ public final class LoanProductDataValidator {
             
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue())
                     
.value(incomeFromChargeOffPenaltyAccountId).ignoreIfNull().integerGreaterThanZero();
 
+            final Long incomeFromGoodwillCreditInterestAccountId = 
this.fromApiJsonHelper
+                    
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
 element);
+            
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue())
+                    
.value(incomeFromGoodwillCreditInterestAccountId).ignoreIfNull().integerGreaterThanZero();
+
+            final Long incomeFromGoodwillCreditFeesAccountId = 
this.fromApiJsonHelper
+                    
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
 element);
+            
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue())
+                    
.value(incomeFromGoodwillCreditFeesAccountId).ignoreIfNull().integerGreaterThanZero();
+
+            final Long incomeFromGoodwillCreditPenaltyAccountId = 
this.fromApiJsonHelper
+                    
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
 element);
+            
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue())
+                    
.value(incomeFromGoodwillCreditPenaltyAccountId).ignoreIfNull().integerGreaterThanZero();
+
             final Long chargeOffExpenseAccountId = this.fromApiJsonHelper
                     
.extractLongNamed(LoanProductAccountingParams.CHARGE_OFF_EXPENSE.getValue(), 
element);
             
baseDataValidator.reset().parameter(LoanProductAccountingParams.CHARGE_OFF_EXPENSE.getValue()).value(chargeOffExpenseAccountId)
@@ -1528,6 +1547,21 @@ public final class LoanProductDataValidator {
         
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_CHARGE_OFF_PENALTY.getValue())
                 
.value(incomeFromChargeOffPenaltyAccountId).ignoreIfNull().integerGreaterThanZero();
 
+        final Long incomeFromGoodwillCreditInterestAccountId = 
this.fromApiJsonHelper
+                
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
 element);
+        
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue())
+                
.value(incomeFromGoodwillCreditInterestAccountId).ignoreIfNull().integerGreaterThanZero();
+
+        final Long incomeFromGoodwillCreditFeesAccountId = 
this.fromApiJsonHelper
+                
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
 element);
+        
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue())
+                
.value(incomeFromGoodwillCreditFeesAccountId).ignoreIfNull().integerGreaterThanZero();
+
+        final Long incomeFromGoodwillCreditPenaltyAccountId = 
this.fromApiJsonHelper
+                
.extractLongNamed(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
 element);
+        
baseDataValidator.reset().parameter(LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue())
+                
.value(incomeFromGoodwillCreditPenaltyAccountId).ignoreIfNull().integerGreaterThanZero();
+
         final Long chargeOffExpenseAccountId = this.fromApiJsonHelper
                 
.extractLongNamed(LoanProductAccountingParams.CHARGE_OFF_EXPENSE.getValue(), 
element);
         
baseDataValidator.reset().parameter(LoanProductAccountingParams.CHARGE_OFF_EXPENSE.getValue()).value(chargeOffExpenseAccountId)
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
index df7637ebe..19bc77cda 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
@@ -429,6 +429,7 @@ public class 
ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
         final Float totalOutstanding = (Float) 
loanSummaryMap.get("totalOutstanding");
         final Float overpaidAmount = 159.00f;
         final Float goodwillAmount = totalOutstanding + overpaidAmount;
+        final Float goodwillAmountInExpense = principalOutstanding + 
overpaidAmount;
         final String goodwillDate = "09 January 2022";
         HashMap loanStatusHashMap = (HashMap) 
this.loanTransactionHelper.makeRepaymentTypePayment(GOODWILL_CREDIT, 
goodwillDate,
                 goodwillAmount, this.disbursedLoanID, "");
@@ -439,7 +440,7 @@ public class 
ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
         
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount,
 goodwillDate,
                 new JournalEntry(overpaidAmount, 
JournalEntry.TransactionType.CREDIT));
         
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, 
goodwillDate,
-                new JournalEntry(goodwillAmount, 
JournalEntry.TransactionType.DEBIT));
+                new JournalEntry(goodwillAmountInExpense, 
JournalEntry.TransactionType.DEBIT));
 
     }
 
@@ -455,6 +456,7 @@ public class 
ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
         final Float totalOutstanding = (Float) 
loanSummaryMap.get("totalOutstanding");
         final Float overpaidAmount = 159.00f;
         final Float goodwillAmount = totalOutstanding + overpaidAmount;
+        final Float goodwillAmountInExpense = principalOutstanding + 
overpaidAmount;
         final String goodwillDate = "09 January 2022";
         HashMap loanStatusHashMap = (HashMap) 
this.loanTransactionHelper.makeRepaymentTypePayment(GOODWILL_CREDIT, 
goodwillDate,
                 goodwillAmount, this.disbursedLoanID, "");
@@ -467,7 +469,7 @@ public class 
ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
         
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount,
 goodwillDate,
                 new JournalEntry(overpaidAmount, 
JournalEntry.TransactionType.CREDIT));
         
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, 
goodwillDate,
-                new JournalEntry(goodwillAmount, 
JournalEntry.TransactionType.DEBIT));
+                new JournalEntry(goodwillAmountInExpense, 
JournalEntry.TransactionType.DEBIT));
 
     }
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
index 2ee0379a2..ccbfb0988 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeOffAccountingTest.java
@@ -91,7 +91,7 @@ public class LoanChargeOffAccountingTest {
         // Product to GL account mapping for test
         // ASSET
         // 
-fundSourceAccountId,loanPortfolioAccountId,transfersInSuspenseAccountId,receivableFeeAccountId,receivablePenaltyAccountId,receivableInterestAccountId
-        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId
+        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId,incomeFromGoodwillCreditInterestAccountId,incomeFromGoodwillCreditFeesAccountId,incomeFromGoodwillCreditPenaltyAccountId
         // 
EXPENSE-writeOffAccountId,goodwillCreditAccountId,chargeOffExpenseAccountId,chargeOffFraudExpenseAccountId
         // LIABILITY-overpaymentLiabilityAccountId
 
@@ -135,9 +135,7 @@ public class LoanChargeOffAccountingTest {
         
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
                 new JournalEntry(1000, JournalEntry.TransactionType.DEBIT));
         
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
-                new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
-        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
-                new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
 
         // make Repayment
         final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
@@ -246,7 +244,7 @@ public class LoanChargeOffAccountingTest {
         // Product to GL account mapping for test
         // ASSET
         // 
-fundSourceAccountId,loanPortfolioAccountId,transfersInSuspenseAccountId
-        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId
+        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId,incomeFromGoodwillCreditInterestAccountId,incomeFromGoodwillCreditFeesAccountId,incomeFromGoodwillCreditPenaltyAccountId
         // 
EXPENSE-writeOffAccountId,goodwillCreditAccountId,chargeOffExpenseAccountId,chargeOffFraudExpenseAccountId
         // LIABILITY-overpaymentLiabilityAccountId
 
@@ -303,9 +301,7 @@ public class LoanChargeOffAccountingTest {
         
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
                 new JournalEntry(1000, JournalEntry.TransactionType.DEBIT));
         
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
-                new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
-        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
-                new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
 
         // make Repayment
         final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
@@ -411,6 +407,275 @@ public class LoanChargeOffAccountingTest {
                 new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
     }
 
+    // Tests for Goodwill Credit accounting changes
+    @Test
+    public void 
loanAccountingTreatmentTestForGoodwillCreditPeriodicAccrualAccounting_NoChargeOff()
 {
+        // Loan ExternalId
+        String loanExternalIdStr = UUID.randomUUID().toString();
+
+        // Product to GL account mapping for test
+        // ASSET
+        // 
-fundSourceAccountId,loanPortfolioAccountId,transfersInSuspenseAccountId,receivableFeeAccountId,receivablePenaltyAccountId,receivableInterestAccountId
+        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId,incomeFromGoodwillCreditInterestAccountId,incomeFromGoodwillCreditFeesAccountId,incomeFromGoodwillCreditPenaltyAccountId
+        // 
EXPENSE-writeOffAccountId,goodwillCreditAccountId,chargeOffExpenseAccountId,chargeOffFraudExpenseAccountId
+        // LIABILITY-overpaymentLiabilityAccountId
+
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccounting(assetAccount, incomeAccount, 
expenseAccount,
+                overpaymentAccount);
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // apply charges
+        Integer feeCharge = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+        LocalDate targetDate = LocalDate.of(2022, 9, 5);
+        final String feeCharge1AddedDate = dateFormatter.format(targetDate);
+        Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+        // apply penalty
+        Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "110", true));
+
+        final String penaltyCharge1AddedDate = 
dateFormatter.format(targetDate);
+
+        Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // Goodwill Credit
+        final PostLoansLoanIdTransactionsResponse goodwillCredit_1 = 
loanTransactionHelper.makeGoodwillCredit((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("06 September 2022").locale("en")
+                        .transactionAmount(800.0));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // verify Journal Entries for Goodwill Credit
+        this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, 
"6 September 2022",
+                new JournalEntry(800, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
+                new JournalEntry(780, JournalEntry.TransactionType.DEBIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
+
+    }
+
+    @Test
+    public void 
loanAccountingTreatmentTestForGoodwillCreditPeriodicAccrualAccounting_ChargeOff()
 {
+        // Loan ExternalId
+        String loanExternalIdStr = UUID.randomUUID().toString();
+
+        // Product to GL account mapping for test
+        // ASSET
+        // 
-fundSourceAccountId,loanPortfolioAccountId,transfersInSuspenseAccountId,receivableFeeAccountId,receivablePenaltyAccountId,receivableInterestAccountId
+        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId,incomeFromGoodwillCreditInterestAccountId,incomeFromGoodwillCreditFeesAccountId,incomeFromGoodwillCreditPenaltyAccountId
+        // 
EXPENSE-writeOffAccountId,goodwillCreditAccountId,chargeOffExpenseAccountId,chargeOffFraudExpenseAccountId
+        // LIABILITY-overpaymentLiabilityAccountId
+
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrualAccounting(assetAccount, incomeAccount, 
expenseAccount,
+                overpaymentAccount);
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // apply charges
+        Integer feeCharge = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+        LocalDate targetDate = LocalDate.of(2022, 9, 5);
+        final String feeCharge1AddedDate = dateFormatter.format(targetDate);
+        Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+        // apply penalty
+        Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
+
+        final String penaltyCharge1AddedDate = 
dateFormatter.format(targetDate);
+
+        Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+        // set loan as chargeoff
+        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();
+        this.loanTransactionHelper.chargeOffLoan((long) loanId, new 
PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022")
+                .locale("en").dateFormat("dd MMMM 
yyyy").externalId(transactionExternalId).chargeOffReasonId((long) 
chargeOffReasonId));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries For ChargeOff Transaction
+        this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, 
"6 September 2022",
+                new JournalEntry(1020, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
+                new JournalEntry(1000, JournalEntry.TransactionType.DEBIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
+
+        // Goodwill Credit
+        final PostLoansLoanIdTransactionsResponse goodwillCredit_1 = 
loanTransactionHelper.makeGoodwillCredit((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("08 September 2022").locale("en")
+                        .transactionAmount(800.0));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries for Goodwill Credit
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "8 
September 2022",
+                new JournalEntry(800, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "8 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
+        
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "8 
September 2022",
+                new JournalEntry(780, JournalEntry.TransactionType.DEBIT));
+
+    }
+
+    @Test
+    public void 
loanAccountingTreatmentTestForCashBasedAccounting_NoChargeOff() {
+        // Loan ExternalId
+        String loanExternalIdStr = UUID.randomUUID().toString();
+
+        // Product to GL account mapping for test
+        // ASSET
+        // 
-fundSourceAccountId,loanPortfolioAccountId,transfersInSuspenseAccountId
+        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId,incomeFromGoodwillCreditInterestAccountId,incomeFromGoodwillCreditFeesAccountId,incomeFromGoodwillCreditPenaltyAccountId
+        // 
EXPENSE-writeOffAccountId,goodwillCreditAccountId,chargeOffExpenseAccountId,chargeOffFraudExpenseAccountId
+        // LIABILITY-overpaymentLiabilityAccountId
+
+        final Integer loanProductID = 
createLoanProductWithCashBasedAccounting(assetAccount, incomeAccount, 
expenseAccount,
+                overpaymentAccount);
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // apply charges
+        Integer feeCharge = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+        LocalDate targetDate = LocalDate.of(2022, 9, 5);
+        final String feeCharge1AddedDate = dateFormatter.format(targetDate);
+        Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+        // apply penalty
+        Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
+
+        final String penaltyCharge1AddedDate = 
dateFormatter.format(targetDate);
+
+        Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // Goodwill Credit
+        final PostLoansLoanIdTransactionsResponse goodwillCredit_1 = 
loanTransactionHelper.makeGoodwillCredit((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("06 September 2022").locale("en")
+                        .transactionAmount(800.0));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // verify Journal Entries for Goodwill Credit
+        this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, 
"6 September 2022",
+                new JournalEntry(780, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(10, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(10, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
+                new JournalEntry(780, JournalEntry.TransactionType.DEBIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
+
+    }
+
+    @Test
+    public void loanAccountingTreatmentTestForCashBasedAccounting_ChargeOff() {
+        // Loan ExternalId
+        String loanExternalIdStr = UUID.randomUUID().toString();
+
+        // Product to GL account mapping for test
+        // ASSET
+        // 
-fundSourceAccountId,loanPortfolioAccountId,transfersInSuspenseAccountId
+        // 
INCOME-interestOnLoanAccountId,incomeFromFeeAccountId,incomeFromPenaltyAccountId,incomeFromRecoveryAccountId,incomeFromChargeOffInterestAccountId,incomeFromChargeOffFeesAccountId,incomeFromChargeOffPenaltyAccountId,incomeFromGoodwillCreditInterestAccountId,incomeFromGoodwillCreditFeesAccountId,incomeFromGoodwillCreditPenaltyAccountId
+        // 
EXPENSE-writeOffAccountId,goodwillCreditAccountId,chargeOffExpenseAccountId,chargeOffFraudExpenseAccountId
+        // LIABILITY-overpaymentLiabilityAccountId
+
+        final Integer loanProductID = 
createLoanProductWithCashBasedAccounting(assetAccount, incomeAccount, 
expenseAccount,
+                overpaymentAccount);
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+        final Integer loanId = createLoanAccount(clientId, loanProductID, 
loanExternalIdStr);
+
+        // apply charges
+        Integer feeCharge = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+        LocalDate targetDate = LocalDate.of(2022, 9, 5);
+        final String feeCharge1AddedDate = dateFormatter.format(targetDate);
+        Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge),
 feeCharge1AddedDate, "10"));
+
+        // apply penalty
+        Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
+
+        final String penaltyCharge1AddedDate = 
dateFormatter.format(targetDate);
+
+        Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+        GetLoansLoanIdResponse loanDetails = 
this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+
+        // set loan as chargeoff
+        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();
+        this.loanTransactionHelper.chargeOffLoan((long) loanId, new 
PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022")
+                .locale("en").dateFormat("dd MMMM 
yyyy").externalId(transactionExternalId).chargeOffReasonId((long) 
chargeOffReasonId));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries For ChargeOff Transaction
+        this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, 
"6 September 2022",
+                new JournalEntry(1000, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
+                new JournalEntry(1000, JournalEntry.TransactionType.DEBIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
+
+        // Goodwill Credit
+        final PostLoansLoanIdTransactionsResponse goodwillCredit_1 = 
loanTransactionHelper.makeGoodwillCredit((long) loanId,
+                new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM 
yyyy").transactionDate("10 September 2022").locale("en")
+                        .transactionAmount(800.0));
+
+        loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
+        assertTrue(loanDetails.getStatus().getActive());
+        assertTrue(loanDetails.getChargedOff());
+
+        // verify Journal Entries for Goodwill Credit
+        this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, 
"6 September 2022",
+                new JournalEntry(1000, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.CREDIT));
+        
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "6 
September 2022",
+                new JournalEntry(1000, JournalEntry.TransactionType.DEBIT));
+        
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 
September 2022",
+                new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
+    }
+
     private Integer createLoanAccount(final Integer clientID, final Integer 
loanProductID, final String externalId) {
 
         String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
index 7a2d50b1e..d5f9fb637 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java
@@ -446,6 +446,9 @@ public class LoanProductTestBuilder {
                 map.put("incomeFromChargeOffInterestAccountId", ID);
                 map.put("incomeFromChargeOffFeesAccountId", ID);
                 map.put("incomeFromChargeOffPenaltyAccountId", ID);
+                map.put("incomeFromGoodwillCreditInterestAccountId", ID);
+                map.put("incomeFromGoodwillCreditFeesAccountId", ID);
+                map.put("incomeFromGoodwillCreditPenaltyAccountId", ID);
             }
             if 
(this.accountList[i].getAccountType().equals(Account.AccountType.EXPENSE)) {
                 final String ID = 
this.accountList[i].getAccountID().toString();
@@ -489,6 +492,9 @@ public class LoanProductTestBuilder {
                 map.put("incomeFromChargeOffInterestAccountId", ID);
                 map.put("incomeFromChargeOffFeesAccountId", ID);
                 map.put("incomeFromChargeOffPenaltyAccountId", ID);
+                map.put("incomeFromGoodwillCreditInterestAccountId", ID);
+                map.put("incomeFromGoodwillCreditFeesAccountId", ID);
+                map.put("incomeFromGoodwillCreditPenaltyAccountId", ID);
             }
             if 
(this.accountList[i].getAccountType().equals(Account.AccountType.EXPENSE)) {
                 final String ID = 
this.accountList[i].getAccountID().toString();

Reply via email to