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

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

commit 8123276981bf466e23ebd7b3090073a634353251
Author: Adam Saghy <[email protected]>
AuthorDate: Tue Dec 17 18:22:28 2024 +0100

    FINERACT-1806: Rework Product to GL account mapping and fix charge-off 
reason typos
---
 .../domain/ProductToGLAccountMapping.java          |   4 +-
 .../ProductToGLAccountMappingRepository.java       |  19 ++--
 .../service/ProductToGLAccountMappingHelper.java   |  14 +--
 ...tToGLAccountMappingReadPlatformServiceImpl.java |  45 ++++----
 .../accounting/common/AccountingConstants.java     |   2 +-
 .../data/ChargeOffReasonToGLAccountMapper.java     |   2 +-
 .../api/LoanProductsApiResourceSwagger.java        |  14 +--
 .../loanproduct/data/LoanProductData.java          |   8 +-
 .../service/AccountingProcessorHelper.java         |   2 +-
 .../AccrualBasedAccountingProcessorForLoan.java    |   4 +-
 .../CashBasedAccountingProcessorForLoan.java       | 119 ++++++++++-----------
 .../loanproduct/api/LoanProductsApiResource.java   |   2 +-
 .../serialization/LoanProductDataValidator.java    |   4 +-
 .../LoanProductChargeOffReasonMappingsTest.java    | 105 ++++++++----------
 .../common/loans/LoanProductTestBuilder.java       |  14 +--
 15 files changed, 161 insertions(+), 197 deletions(-)

diff --git 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java
 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java
index 5e953ff27..1f602e90d 100644
--- 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java
+++ 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMapping.java
@@ -69,9 +69,9 @@ public class ProductToGLAccountMapping extends 
AbstractPersistableCustom<Long> {
     private CodeValue chargeOffReason;
 
     public static ProductToGLAccountMapping createNew(final GLAccount 
glAccount, final Long productId, final int productType,
-            final int financialAccountType, final CodeValue chargeOffReasonId) 
{
+            final int financialAccountType, final CodeValue chargeOffReason) {
 
         return new 
ProductToGLAccountMapping().setGlAccount(glAccount).setProductId(productId).setProductType(productType)
-                
.setFinancialAccountType(financialAccountType).setChargeOffReason(chargeOffReasonId);
+                
.setFinancialAccountType(financialAccountType).setChargeOffReason(chargeOffReason);
     }
 }
diff --git 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java
 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java
index 0dd2b6fb8..9a5436e6c 100644
--- 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java
+++ 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/domain/ProductToGLAccountMappingRepository.java
@@ -63,19 +63,22 @@ public interface ProductToGLAccountMappingRepository
     List<ProductToGLAccountMapping> findByProductIdAndProductType(Long 
productId, int productType);
 
     @Query("select mapping from ProductToGLAccountMapping mapping where 
mapping.productId =:productId and mapping.productType =:productType and 
mapping.chargeOffReason is not NULL")
-    List<ProductToGLAccountMapping> 
findAllChargesOffReasonsMappings(@Param("productId") Long productId,
+    List<ProductToGLAccountMapping> 
findAllChargeOffReasonsMappings(@Param("productId") Long productId,
             @Param("productType") int productType);
 
     @Query("select mapping from ProductToGLAccountMapping mapping where 
mapping.chargeOffReason.id =:chargeOffReasonId")
-    ProductToGLAccountMapping 
findChargesOffReasonMappingById(@Param("chargeOffReasonId") Integer 
chargeOffReasonId);
+    ProductToGLAccountMapping 
findChargeOffReasonMappingById(@Param("chargeOffReasonId") Integer 
chargeOffReasonId);
 
-    List<ProductToGLAccountMapping> 
findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(Long 
productId,
-            Integer productType);
+    @Query("select mapping from ProductToGLAccountMapping mapping where 
mapping.productId =:productId AND mapping.productType =:productType AND 
mapping.charge IS NULL AND mapping.paymentType IS NULL AND 
mapping.chargeOffReason IS NULL")
+    List<ProductToGLAccountMapping> findAllRegularMappings(@Param("productId") 
Long productId, @Param("productType") Integer productType);
 
-    List<ProductToGLAccountMapping> 
findAllByProductIdAndProductTypeAndChargeOffReasonIdIsNotNull(Long productId, 
Integer productType);
+    @Query("select mapping from ProductToGLAccountMapping mapping where 
mapping.productId =:productId and mapping.productType =:productType and 
mapping.paymentType is not NULL")
+    List<ProductToGLAccountMapping> 
findAllPaymentTypeMappings(@Param("productId") Long productId,
+            @Param("productType") Integer productType);
 
-    List<ProductToGLAccountMapping> 
findAllByProductIdAndProductTypeAndPaymentTypeIsNotNull(Long productId, Integer 
productType);
+    @Query("select mapping from ProductToGLAccountMapping mapping where 
mapping.productId =:productId AND mapping.productType =:productType AND 
mapping.charge.penalty = TRUE")
+    List<ProductToGLAccountMapping> findAllPenaltyMappings(@Param("productId") 
Long productId, @Param("productType") Integer productType);
 
-    List<ProductToGLAccountMapping> 
findAllByProductIdAndProductTypeAndCharge_PenaltyAndCharge_IdIsNotNull(Long 
productId,
-            Integer productType, boolean isPenalty);
+    @Query("select mapping from ProductToGLAccountMapping mapping where 
mapping.productId =:productId AND mapping.productType =:productType AND 
mapping.charge.penalty = FALSE")
+    List<ProductToGLAccountMapping> findAllFeeMappings(@Param("productId") 
Long productId, @Param("productType") Integer productType);
 }
diff --git 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
index 34b9e06d7..52d0a201b 100644
--- 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
+++ 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingHelper.java
@@ -204,7 +204,7 @@ public class ProductToGLAccountMappingHelper {
     public void saveChargeOffReasonToGLAccountMappings(final JsonCommand 
command, final JsonElement element, final Long productId,
             final Map<String, Object> changes, final PortfolioProductType 
portfolioProductType) {
 
-        final String arrayName = 
LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue();
+        final String arrayName = 
LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue();
         final JsonArray chargeOffReasonToExpenseAccountMappingArray = 
this.fromApiJsonHelper.extractJsonArrayNamed(arrayName, element);
 
         if (chargeOffReasonToExpenseAccountMappingArray != null) {
@@ -388,17 +388,17 @@ public class ProductToGLAccountMappingHelper {
             final Map<String, Object> changes, final PortfolioProductType 
portfolioProductType) {
 
         final List<ProductToGLAccountMapping> 
existingChargeOffReasonToGLAccountMappings = this.accountMappingRepository
-                .findAllChargesOffReasonsMappings(productId, 
portfolioProductType.getValue());
+                .findAllChargeOffReasonsMappings(productId, 
portfolioProductType.getValue());
         final JsonArray chargeOffReasonToGLAccountMappingArray = 
this.fromApiJsonHelper
-                
.extractJsonArrayNamed(LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue(),
 element);
+                
.extractJsonArrayNamed(LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue(),
 element);
 
         final Map<Long, Long> inputChargeOffReasonToGLAccountMap = new 
HashMap<>();
 
         final Set<Long> existingChargeOffReasons = new HashSet<>();
         if (chargeOffReasonToGLAccountMappingArray != null) {
             if (changes != null) {
-                
changes.put(LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue(),
-                        
command.jsonFragment(LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue()));
+                
changes.put(LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue(),
+                        
command.jsonFragment(LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue()));
             }
 
             for (int i = 0; i < chargeOffReasonToGLAccountMappingArray.size(); 
i++) {
@@ -495,7 +495,7 @@ public class ProductToGLAccountMappingHelper {
         final Optional<GLAccount> glAccount = 
accountRepository.findById(expenseAccountId);
 
         final boolean reasonMappingExists = this.accountMappingRepository
-                .findAllChargesOffReasonsMappings(productId, 
portfolioProductType.getValue()).stream()
+                .findAllChargeOffReasonsMappings(productId, 
portfolioProductType.getValue()).stream()
                 .anyMatch(mapping -> 
mapping.getChargeOffReason().getId().equals(reasonId));
 
         final Optional<CodeValue> codeValueOptional = 
codeValueRepository.findById(reasonId);
@@ -577,7 +577,7 @@ public class ProductToGLAccountMappingHelper {
             if (codeValue == null) {
                 
validationErrors.add(ApiParameterError.parameterError("validation.msg.chargeoffreason.invalid",
                         "Charge-off reason with ID " + 
chargeOffReasonCodeValueId + " does not exist",
-                        
LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue()));
+                        
LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue()));
             }
 
             // Validation: expenseGLAccountId must exist as a valid Expense GL 
account
diff --git 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
index fd3cbfd85..9b19c65ab 100644
--- 
a/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
+++ 
b/fineract-accounting/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/service/ProductToGLAccountMappingReadPlatformServiceImpl.java
@@ -61,8 +61,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl 
implements Product
 
         final Map<String, Object> accountMappingDetails = new 
LinkedHashMap<>(8);
 
-        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository
-                
.findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(loanProductId,
 PortfolioProductType.LOAN.getValue());
+        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository.findAllRegularMappings(loanProductId,
+                PortfolioProductType.LOAN.getValue());
 
         if (AccountingValidations.isCashBasedAccounting(accountingType)) {
 
@@ -178,9 +178,8 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
     @Override
     public Map<String, Object> 
fetchAccountMappingDetailsForSavingsProduct(final Long savingsProductId, final 
Integer accountingType) {
 
-        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository
-                
.findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(savingsProductId,
-                        PortfolioProductType.SAVING.getValue());
+        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository.findAllRegularMappings(savingsProductId,
+                PortfolioProductType.SAVING.getValue());
 
         Map<String, Object> accountMappingDetails = null;
         if (AccountingValidations.isCashBasedAccounting(accountingType)) {
@@ -210,14 +209,11 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
      */
     private List<PaymentTypeToGLAccountMapper> 
fetchPaymentTypeToFundSourceMappings(final PortfolioProductType 
portfolioProductType,
             final Long loanProductId) {
-        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository
-                
.findAllByProductIdAndProductTypeAndPaymentTypeIsNotNull(loanProductId, 
portfolioProductType.getValue());
+        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository.findAllPaymentTypeMappings(loanProductId,
+                portfolioProductType.getValue());
 
-        List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = 
null;
+        List<PaymentTypeToGLAccountMapper> paymentTypeToGLAccountMappers = 
mappings.isEmpty() ? null : new ArrayList<>();
         for (final ProductToGLAccountMapping mapping : mappings) {
-            if (paymentTypeToGLAccountMappers == null) {
-                paymentTypeToGLAccountMappers = new ArrayList<>();
-            }
             final PaymentTypeData paymentTypeData = 
PaymentTypeData.instance(mapping.getPaymentType().getId(),
                     mapping.getPaymentType().getName());
             final GLAccountData gLAccountData = new 
GLAccountData().setId(mapping.getGlAccount().getId())
@@ -252,14 +248,12 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
 
     private List<ChargeToGLAccountMapper> 
fetchChargeToIncomeAccountMappings(final PortfolioProductType 
portfolioProductType,
             final Long loanProductId, final boolean penalty) {
-        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository
-                
.findAllByProductIdAndProductTypeAndCharge_PenaltyAndCharge_IdIsNotNull(loanProductId,
 portfolioProductType.getValue(),
-                        penalty);
-        List<ChargeToGLAccountMapper> chargeToGLAccountMappers = null;
+        final List<ProductToGLAccountMapping> mappings = penalty
+                ? 
productToGLAccountMappingRepository.findAllPenaltyMappings(loanProductId, 
portfolioProductType.getValue())
+                : 
productToGLAccountMappingRepository.findAllFeeMappings(loanProductId, 
portfolioProductType.getValue());
+
+        List<ChargeToGLAccountMapper> chargeToGLAccountMappers = 
mappings.isEmpty() ? null : new ArrayList<>();
         for (final ProductToGLAccountMapping mapping : mappings) {
-            if (chargeToGLAccountMappers == null) {
-                chargeToGLAccountMappers = new ArrayList<>();
-            }
             final GLAccountData gLAccountData = new 
GLAccountData().setId(mapping.getGlAccount().getId())
                     
.setName(mapping.getGlAccount().getName()).setGlCode(mapping.getGlAccount().getGlCode());
             final ChargeData chargeData = 
ChargeData.builder().id(mapping.getCharge().getId()).name(mapping.getCharge().getName())
@@ -273,13 +267,10 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
 
     private List<ChargeOffReasonToGLAccountMapper> 
fetchChargeOffReasonMappings(final PortfolioProductType portfolioProductType,
             final Long loanProductId) {
-        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository
-                
.findAllByProductIdAndProductTypeAndChargeOffReasonIdIsNotNull(loanProductId, 
portfolioProductType.getValue());
-        List<ChargeOffReasonToGLAccountMapper> 
chargeOffReasonToGLAccountMappers = null;
+        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository.findAllChargeOffReasonsMappings(loanProductId,
+                portfolioProductType.getValue());
+        List<ChargeOffReasonToGLAccountMapper> 
chargeOffReasonToGLAccountMappers = mappings.isEmpty() ? null : new 
ArrayList<>();
         for (final ProductToGLAccountMapping mapping : mappings) {
-            if (chargeOffReasonToGLAccountMappers == null) {
-                chargeOffReasonToGLAccountMappers = new ArrayList<>();
-            }
             final Long glAccountId = mapping.getGlAccount().getId();
             final String glAccountName = mapping.getGlAccount().getName();
             final String glCode = mapping.getGlAccount().getGlCode();
@@ -294,7 +285,7 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
                     
.description(codeDescription).position(orderPosition).active(isActive).mandatory(isMandatory).build();
 
             final ChargeOffReasonToGLAccountMapper 
chargeOffReasonToGLAccountMapper = new ChargeOffReasonToGLAccountMapper()
-                    
.setChargeOffReasonsCodeValue(chargeOffReasonsCodeValue).setChargeOffExpenseAccount(chargeOffExpenseAccount);
+                    
.setChargeOffReasonCodeValue(chargeOffReasonsCodeValue).setChargeOffExpenseAccount(chargeOffExpenseAccount);
             
chargeOffReasonToGLAccountMappers.add(chargeOffReasonToGLAccountMapper);
         }
         return chargeOffReasonToGLAccountMappers;
@@ -305,8 +296,8 @@ public class 
ProductToGLAccountMappingReadPlatformServiceImpl implements Product
 
         final Map<String, Object> accountMappingDetails = new 
LinkedHashMap<>(8);
 
-        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository
-                
.findAllByProductIdAndProductTypeAndPaymentTypeIsNullAndChargeIsNull(productId, 
PortfolioProductType.SHARES.getValue());
+        final List<ProductToGLAccountMapping> mappings = 
productToGLAccountMappingRepository.findAllRegularMappings(productId,
+                PortfolioProductType.SHARES.getValue());
 
         if (AccountingRuleType.CASH_BASED.getValue().equals(accountingType)) {
             for (final ProductToGLAccountMapping mapping : mappings) {
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
 
b/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
index ce7a2ff9f..7976659c3 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
@@ -173,7 +173,7 @@ public final class AccountingConstants {
         
INCOME_FROM_GOODWILL_CREDIT_INTEREST("incomeFromGoodwillCreditInterestAccountId"),
 //
         
INCOME_FROM_GOODWILL_CREDIT_FEES("incomeFromGoodwillCreditFeesAccountId"), //
         
INCOME_FROM_GOODWILL_CREDIT_PENALTY("incomeFromGoodwillCreditPenaltyAccountId"),
 //
-        CHARGE_OFF_REASONS_TO_EXPENSE("chargeOffReasonsToExpenseMappings"), //
+        
CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS("chargeOffReasonToExpenseAccountMappings"),
 //
         EXPENSE_GL_ACCOUNT_ID("expenseGLAccountId"), //
         CHARGE_OFF_REASON_CODE_VALUE_ID("chargeOffReasonCodeValueId"); //
 
diff --git 
a/fineract-core/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/data/ChargeOffReasonToGLAccountMapper.java
 
b/fineract-core/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/data/ChargeOffReasonToGLAccountMapper.java
index bf881a3fe..f95e933f2 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/data/ChargeOffReasonToGLAccountMapper.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/accounting/producttoaccountmapping/data/ChargeOffReasonToGLAccountMapper.java
@@ -31,6 +31,6 @@ import 
org.apache.fineract.infrastructure.codes.data.CodeValueData;
 public class ChargeOffReasonToGLAccountMapper implements Serializable {
 
     private static final long serialVersionUID = 1L;
-    private CodeValueData chargeOffReasonsCodeValue;
+    private CodeValueData chargeOffReasonCodeValue;
     private GLAccountData chargeOffExpenseAccount;
 }
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 0558a7a56..76e7c177a 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -248,7 +248,7 @@ final class LoanProductsApiResourceSwagger {
         public Long incomeFromGoodwillCreditPenaltyAccountId;
         public 
List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings>
 paymentChannelToFundSourceMappings;
         public 
List<GetLoanProductsProductIdResponse.GetLoanFeeToIncomeAccountMappings> 
feeToIncomeAccountMappings;
-        public List<PostChargeOffReasonsToExpenseMappings> 
chargeOffReasonsToExpenseMappings;
+        public List<PostChargeOffReasonToExpenseAccountMappings> 
chargeOffReasonToExpenseAccountMappings;
         public List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
 
         // Multi Disburse
@@ -326,9 +326,9 @@ final class LoanProductsApiResourceSwagger {
         @Schema(example = "REGULAR")
         public String chargeOffBehaviour;
 
-        static final class PostChargeOffReasonsToExpenseMappings {
+        static final class PostChargeOffReasonToExpenseAccountMappings {
 
-            private PostChargeOffReasonsToExpenseMappings() {}
+            private PostChargeOffReasonToExpenseAccountMappings() {}
 
             @Schema(example = "1")
             public Long chargeOffReasonCodeValueId;
@@ -1229,9 +1229,9 @@ final class LoanProductsApiResourceSwagger {
             public Long fundSourceAccountId;
         }
 
-        static final class GetChargeOffReasonsToExpenseMappings {
+        static final class GetChargeOffReasonToExpenseAccountMappings {
 
-            private GetChargeOffReasonsToExpenseMappings() {}
+            private GetChargeOffReasonToExpenseAccountMappings() {}
 
             public GetCodeValueData chargeOffReasonCodeValue;
             public GetGLAccountData chargeOffExpenseAccount;
@@ -1376,7 +1376,7 @@ final class LoanProductsApiResourceSwagger {
         public GetLoanAccountingMappings accountingMappings;
         public Set<GetLoanPaymentChannelToFundSourceMappings> 
paymentChannelToFundSourceMappings;
         public Set<GetLoanFeeToIncomeAccountMappings> 
feeToIncomeAccountMappings;
-        public Set<GetChargeOffReasonsToExpenseMappings> 
chargeOffReasonToGLAccountMappings;
+        public List<GetChargeOffReasonToExpenseAccountMappings> 
chargeOffReasonToExpenseAccountMappings;
         @Schema(example = "false")
         public Boolean isRatesEnabled;
         @Schema(example = "true")
@@ -1636,7 +1636,7 @@ final class LoanProductsApiResourceSwagger {
         public Long incomeFromChargeOffPenaltyAccountId;
         public 
List<GetLoanProductsProductIdResponse.GetLoanPaymentChannelToFundSourceMappings>
 paymentChannelToFundSourceMappings;
         public 
List<GetLoanProductsProductIdResponse.GetLoanFeeToIncomeAccountMappings> 
feeToIncomeAccountMappings;
-        public 
List<PostLoanProductsRequest.PostChargeOffReasonsToExpenseMappings> 
chargeOffReasonsToExpenseMappings;
+        public 
List<PostLoanProductsRequest.PostChargeOffReasonToExpenseAccountMappings> 
chargeOffReasonToExpenseAccountMappings;
         public List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
         @Schema(example = "false")
         public Boolean enableAccrualActivityPosting;
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
index 3a1d14bed..31191601e 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/data/LoanProductData.java
@@ -154,7 +154,7 @@ public class LoanProductData implements Serializable {
     private Collection<PaymentTypeToGLAccountMapper> 
paymentChannelToFundSourceMappings;
     private Collection<ChargeToGLAccountMapper> feeToIncomeAccountMappings;
     private Collection<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings;
-    private List<ChargeOffReasonToGLAccountMapper> 
chargeOffReasonToGLAccountMappings;
+    private List<ChargeOffReasonToGLAccountMapper> 
chargeOffReasonToExpenseAccountMappings;
     private final boolean enableAccrualActivityPosting;
 
     // rates
@@ -747,7 +747,7 @@ public class LoanProductData implements Serializable {
         productData.paymentChannelToFundSourceMappings = 
paymentChannelToFundSourceMappings;
         productData.feeToIncomeAccountMappings = feeToGLAccountMappings;
         productData.penaltyToIncomeAccountMappings = 
penaltyToGLAccountMappings;
-        productData.chargeOffReasonToGLAccountMappings = 
chargeOffReasonToGLAccountMappings;
+        productData.chargeOffReasonToExpenseAccountMappings = 
chargeOffReasonToGLAccountMappings;
         return productData;
     }
 
@@ -865,7 +865,7 @@ public class LoanProductData implements Serializable {
         this.paymentChannelToFundSourceMappings = null;
         this.feeToIncomeAccountMappings = null;
         this.penaltyToIncomeAccountMappings = null;
-        this.chargeOffReasonToGLAccountMappings = null;
+        this.chargeOffReasonToExpenseAccountMappings = null;
         this.valueConditionTypeOptions = null;
         this.principalVariationsForBorrowerCycle = principalVariations;
         this.interestRateVariationsForBorrowerCycle = interestRateVariations;
@@ -1008,7 +1008,7 @@ public class LoanProductData implements Serializable {
         this.paymentChannelToFundSourceMappings = 
productData.paymentChannelToFundSourceMappings;
         this.feeToIncomeAccountMappings = 
productData.feeToIncomeAccountMappings;
         this.penaltyToIncomeAccountMappings = 
productData.penaltyToIncomeAccountMappings;
-        this.chargeOffReasonToGLAccountMappings = 
productData.chargeOffReasonToGLAccountMappings;
+        this.chargeOffReasonToExpenseAccountMappings = 
productData.chargeOffReasonToExpenseAccountMappings;
 
         this.chargeOptions = chargeOptions;
         this.penaltyOptions = penaltyOptions;
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
index 1cac8808f..5e32eb780 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccountingProcessorHelper.java
@@ -178,7 +178,7 @@ public class AccountingProcessorHelper {
     }
 
     public ProductToGLAccountMapping getChargeOffMappingByCodeValue(Integer 
chargeOffReasonCodeValue) {
-        return 
accountMappingRepository.findChargesOffReasonMappingById(chargeOffReasonCodeValue);
+        return 
accountMappingRepository.findChargeOffReasonMappingById(chargeOffReasonCodeValue);
     }
 
     public SavingsDTO populateSavingsDtoFromMap(final Map<String, Object> 
accountingBridgeData, final boolean cashBasedAccountingEnabled,
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 eaf22e98d..773fcadf1 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
@@ -232,7 +232,9 @@ public class AccrualBasedAccountingProcessorForLoan 
implements AccountingProcess
         // need to fetch if there are account mappings (always one)
         Integer chargeOffReasonCodeValue = 
loanDTO.getChargeOffReasonCodeValue();
 
-        ProductToGLAccountMapping mapping = 
helper.getChargeOffMappingByCodeValue(chargeOffReasonCodeValue);
+        ProductToGLAccountMapping mapping = chargeOffReasonCodeValue != null
+                ? 
helper.getChargeOffMappingByCodeValue(chargeOffReasonCodeValue)
+                : null;
         if (mapping != null) {
             GLAccount accountCredit = 
this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
                     AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), 
paymentTypeId);
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 4a2f9009c..770194ef7 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
@@ -30,6 +30,7 @@ import 
org.apache.fineract.accounting.common.AccountingConstants.CashAccountsFor
 import 
org.apache.fineract.accounting.common.AccountingConstants.FinancialActivity;
 import org.apache.fineract.accounting.glaccount.domain.GLAccount;
 import org.apache.fineract.accounting.journalentry.data.ChargePaymentDTO;
+import org.apache.fineract.accounting.journalentry.data.GLAccountBalanceHolder;
 import org.apache.fineract.accounting.journalentry.data.LoanDTO;
 import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
 import org.apache.fineract.infrastructure.core.service.MathUtil;
@@ -139,71 +140,59 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
         final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
         final boolean isReversal = loanTransactionDTO.isReversed();
 
-        Map<GLAccount, BigDecimal> accountMapForCredit = new LinkedHashMap<>();
-
-        Map<Integer, BigDecimal> accountMapForDebit = new LinkedHashMap<>();
+        GLAccountBalanceHolder glAccountBalanceHolder = new 
GLAccountBalanceHolder();
 
         // principal payment
         if (principalAmount != null && 
principalAmount.compareTo(BigDecimal.ZERO) > 0) {
             if (isMarkedFraud) {
                 populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
-                        
CashAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), accountMapForCredit, 
accountMapForDebit);
+                        
CashAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), 
glAccountBalanceHolder);
             } else {
                 populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
-                        CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), 
glAccountBalanceHolder);
             }
         }
 
         // interest payment
         if (interestAmount != null && 
interestAmount.compareTo(BigDecimal.ZERO) > 0) {
             populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId, CashAccountsForLoan.INTEREST_ON_LOANS.getValue(),
-                    
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), 
accountMapForCredit, accountMapForDebit);
+                    
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), 
glAccountBalanceHolder);
         }
 
         // handle fees payment
         if (feesAmount != null && feesAmount.compareTo(BigDecimal.ZERO) > 0) {
             populateCreditDebitMaps(loanProductId, feesAmount, paymentTypeId, 
CashAccountsForLoan.INCOME_FROM_FEES.getValue(),
-                    
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), 
accountMapForCredit, accountMapForDebit);
+                    
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), 
glAccountBalanceHolder);
         }
 
         // handle penalties payment
         if (penaltiesAmount != null && 
penaltiesAmount.compareTo(BigDecimal.ZERO) > 0) {
             populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(),
-                    
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
accountMapForCredit, accountMapForDebit);
+                    
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
glAccountBalanceHolder);
         }
 
         // create credit entries
-        for (Map.Entry<GLAccount, BigDecimal> creditEntry : 
accountMapForCredit.entrySet()) {
+        for (Map.Entry<Long, BigDecimal> creditEntry : 
glAccountBalanceHolder.getCreditBalances().entrySet()) {
+            GLAccount glAccount = 
glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
             this.helper.createCreditJournalEntryOrReversalForLoan(office, 
currencyCode, loanId, transactionId, transactionDate,
-                    creditEntry.getValue(), isReversal, creditEntry.getKey());
+                    creditEntry.getValue(), isReversal, glAccount);
         }
-
         // create debit entries
-        for (Map.Entry<Integer, BigDecimal> debitEntry : 
accountMapForDebit.entrySet()) {
-            this.helper.createDebitJournalEntryOrReversalForLoan(office, 
currencyCode, debitEntry.getKey().intValue(), loanProductId,
-                    paymentTypeId, loanId, transactionId, transactionDate, 
debitEntry.getValue(), isReversal);
+        for (Map.Entry<Long, BigDecimal> debitEntry : 
glAccountBalanceHolder.getDebitBalances().entrySet()) {
+            GLAccount glAccount = 
glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
+            this.helper.createDebitJournalEntryOrReversalForLoan(office, 
currencyCode, loanId, transactionId, transactionDate,
+                    debitEntry.getValue(), isReversal, glAccount);
         }
-
     }
 
     private void populateCreditDebitMaps(Long loanProductId, BigDecimal 
transactionPartAmount, Long paymentTypeId,
-            Integer creditAccountType, Integer debitAccountType, 
Map<GLAccount, BigDecimal> accountMapForCredit,
-            Map<Integer, BigDecimal> accountMapForDebit) {
+            Integer creditAccountType, Integer debitAccountType, 
GLAccountBalanceHolder glAccountBalanceHolder) {
+        // Resolve Credit
         GLAccount accountCredit = 
this.helper.getLinkedGLAccountForLoanProduct(loanProductId, creditAccountType, 
paymentTypeId);
-        if (accountMapForCredit.containsKey(accountCredit)) {
-            BigDecimal amount = 
accountMapForCredit.get(accountCredit).add(transactionPartAmount);
-            accountMapForCredit.put(accountCredit, amount);
-        } else {
-            accountMapForCredit.put(accountCredit, transactionPartAmount);
-        }
-        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);
-        }
+        glAccountBalanceHolder.addToCredit(accountCredit, 
transactionPartAmount);
+        // Resolve Debit
+        GLAccount accountDebit = 
this.helper.getLinkedGLAccountForLoanProduct(loanProductId, debitAccountType, 
paymentTypeId);
+        glAccountBalanceHolder.addToDebit(accountDebit, transactionPartAmount);
     }
 
     private Integer returnExistingDebitAccountInMapMatchingGLAccount(Long 
loanProductId, Long paymentTypeId, Integer accountType,
@@ -568,9 +557,7 @@ public class CashBasedAccountingProcessorForLoan implements 
AccountingProcessorF
         final Long paymentTypeId = loanTransactionDTO.getPaymentTypeId();
         final boolean isReversal = loanTransactionDTO.isReversed();
 
-        Map<GLAccount, BigDecimal> accountMapForCredit = new LinkedHashMap<>();
-        Map<Integer, BigDecimal> accountMapForDebit = new LinkedHashMap<>();
-
+        GLAccountBalanceHolder glAccountBalanceHolder = new 
GLAccountBalanceHolder();
         BigDecimal totalDebitAmount = new BigDecimal(0);
 
         // principal payment
@@ -580,35 +567,35 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
                 if (isMarkedFraud) {
                     populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId,
                             
CashAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                            accountMapForCredit, accountMapForDebit);
+                            glAccountBalanceHolder);
                 } else {
                     populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId,
                             CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                            accountMapForCredit, accountMapForDebit);
+                            glAccountBalanceHolder);
                 }
             } else if 
(loanTransactionDTO.getTransactionType().isPayoutRefund()) {
                 if (isMarkedFraud) {
                     populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId,
                             
CashAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                            accountMapForCredit, accountMapForDebit);
+                            glAccountBalanceHolder);
 
                 } else {
                     populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId,
                             CashAccountsForLoan.CHARGE_OFF_EXPENSE.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                            accountMapForCredit, accountMapForDebit);
+                            glAccountBalanceHolder);
                 }
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.GOODWILL_CREDIT.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.GOODWILL_CREDIT.getValue(), 
glAccountBalanceHolder);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
 
             } else {
                 populateCreditDebitMaps(loanProductId, principalAmount, 
paymentTypeId, CashAccountsForLoan.LOAN_PORTFOLIO.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
             }
 
         }
@@ -619,23 +606,23 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
             if 
(loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isPayoutRefund()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_INTEREST.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(), 
glAccountBalanceHolder);
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
 
             } else {
                 populateCreditDebitMaps(loanProductId, interestAmount, 
paymentTypeId, CashAccountsForLoan.INTEREST_ON_LOANS.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
             }
 
         }
@@ -646,24 +633,24 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
             if 
(loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isPayoutRefund()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_FEES.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(), 
glAccountBalanceHolder);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
 
             } else {
                 populateCreditDebitMaps(loanProductId, feesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_FEES.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
             }
 
         }
@@ -674,24 +661,24 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
             if 
(loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isPayoutRefund()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId,
                         
CashAccountsForLoan.INCOME_FROM_CHARGE_OFF_PENALTY.getValue(), 
CashAccountsForLoan.FUND_SOURCE.getValue(),
-                        accountMapForCredit, accountMapForDebit);
+                        glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        
CashAccountsForLoan.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(), 
glAccountBalanceHolder);
 
             } else if (loanTransactionDTO.getTransactionType().isRepayment()) {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_RECOVERY.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
 
             } else {
                 populateCreditDebitMaps(loanProductId, penaltiesAmount, 
paymentTypeId, CashAccountsForLoan.INCOME_FROM_PENALTIES.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
             }
 
         }
@@ -701,26 +688,27 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
             totalDebitAmount = totalDebitAmount.add(overPaymentAmount);
             if 
(loanTransactionDTO.getTransactionType().isMerchantIssuedRefund()) {
                 populateCreditDebitMaps(loanProductId, overPaymentAmount, 
paymentTypeId, CashAccountsForLoan.OVERPAYMENT.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isPayoutRefund()) {
                 populateCreditDebitMaps(loanProductId, overPaymentAmount, 
paymentTypeId, CashAccountsForLoan.OVERPAYMENT.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
 
             } else if 
(loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
                 populateCreditDebitMaps(loanProductId, overPaymentAmount, 
paymentTypeId, CashAccountsForLoan.OVERPAYMENT.getValue(),
-                        CashAccountsForLoan.GOODWILL_CREDIT.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.GOODWILL_CREDIT.getValue(), 
glAccountBalanceHolder);
             } else {
                 populateCreditDebitMaps(loanProductId, overPaymentAmount, 
paymentTypeId, CashAccountsForLoan.OVERPAYMENT.getValue(),
-                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
accountMapForCredit, accountMapForDebit);
+                        CashAccountsForLoan.FUND_SOURCE.getValue(), 
glAccountBalanceHolder);
             }
 
         }
 
         // create credit entries
-        for (Map.Entry<GLAccount, BigDecimal> creditEntry : 
accountMapForCredit.entrySet()) {
+        for (Map.Entry<Long, BigDecimal> creditEntry : 
glAccountBalanceHolder.getCreditBalances().entrySet()) {
+            GLAccount glAccount = 
glAccountBalanceHolder.getGlAccountMap().get(creditEntry.getKey());
             this.helper.createCreditJournalEntryOrReversalForLoan(office, 
currencyCode, loanId, transactionId, transactionDate,
-                    creditEntry.getValue(), isReversal, creditEntry.getKey());
+                    creditEntry.getValue(), isReversal, glAccount);
         }
 
         /*** create a single debit entry (or reversal) for the entire amount 
**/
@@ -732,9 +720,10 @@ public class CashBasedAccountingProcessorForLoan 
implements AccountingProcessorF
                     loanProductId, paymentTypeId, loanId, transactionId, 
transactionDate, totalDebitAmount, isReversal);
         } else {
             // create debit entries
-            for (Map.Entry<Integer, BigDecimal> debitEntry : 
accountMapForDebit.entrySet()) {
-                this.helper.createDebitJournalEntryOrReversalForLoan(office, 
currencyCode, debitEntry.getKey().intValue(), loanProductId,
-                        paymentTypeId, loanId, transactionId, transactionDate, 
debitEntry.getValue(), isReversal);
+            for (Map.Entry<Long, BigDecimal> debitEntry : 
glAccountBalanceHolder.getDebitBalances().entrySet()) {
+                GLAccount glAccount = 
glAccountBalanceHolder.getGlAccountMap().get(debitEntry.getKey());
+                this.helper.createDebitJournalEntryOrReversalForLoan(office, 
currencyCode, loanId, transactionId, transactionDate,
+                        debitEntry.getValue(), isReversal, glAccount);
             }
         }
 
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
index c157731f5..b68a40005 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResource.java
@@ -162,7 +162,7 @@ public class LoanProductsApiResource {
     @Operation(summary = "Create a Loan Product", description = "Depending of 
the Accounting Rule (accountingRule) selected, additional fields with details 
of the appropriate Ledger Account identifiers would need to be passed in.\n"
             + "\n" + "Refer MifosX Accounting Specs Draft for more details 
regarding the significance of the selected accounting rule\n\n"
             + "Mandatory Fields: name, shortName, currencyCode, 
digitsAfterDecimal, inMultiplesOf, principal, numberOfRepayments, 
repaymentEvery, repaymentFrequencyType, interestRatePerPeriod, 
interestRateFrequencyType, amortizationType, interestType, 
interestCalculationPeriodType, transactionProcessingStrategyCode, 
accountingRule, isInterestRecalculationEnabled, daysInYearType, 
daysInMonthType\n\n"
-            + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, 
graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, 
paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, 
penaltyToIncomeAccountMappings, chargeOffReasonsToExpenseMappings, 
includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, 
numberOfRepaymentVariationsForBorrowerCycle, 
interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, 
outstan [...]
+            + "Optional Fields: inArrearsTolerance, graceOnPrincipalPayment, 
graceOnInterestPayment, graceOnInterestCharged, graceOnArrearsAgeing, charges, 
paymentChannelToFundSourceMappings, feeToIncomeAccountMappings, 
penaltyToIncomeAccountMappings, chargeOffReasonToExpenseAccountMappings, 
includeInBorrowerCycle, useBorrowerCycle,principalVariationsForBorrowerCycle, 
numberOfRepaymentVariationsForBorrowerCycle, 
interestRateVariationsForBorrowerCycle, multiDisburseLoan,maxTrancheCount, o 
[...]
             + "Additional Mandatory Fields for Cash(2) based accounting: 
fundSourceAccountId, loanPortfolioAccountId, interestOnLoanAccountId, 
incomeFromFeeAccountId, incomeFromPenaltyAccountId, writeOffAccountId, 
transfersInSuspenseAccountId, overpaymentLiabilityAccountId\n\n"
             + "Additional Mandatory Fields for periodic (3) and upfront 
(4)accrual accounting: fundSourceAccountId, loanPortfolioAccountId, 
interestOnLoanAccountId, incomeFromFeeAccountId, incomeFromPenaltyAccountId, 
writeOffAccountId, receivableInterestAccountId, receivableFeeAccountId, 
receivablePenaltyAccountId, transfersInSuspenseAccountId, 
overpaymentLiabilityAccountId\n\n"
             + "Additional Mandatory Fields if interest recalculation is 
enabled(true): interestRecalculationCompoundingMethod, 
rescheduleStrategyMethod, recalculationRestFrequencyType\n\n"
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 f967e897b..cfc230a33 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
@@ -144,7 +144,7 @@ public final class LoanProductDataValidator {
             
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_INTEREST.getValue(),
             
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_FEES.getValue(),
             
LoanProductAccountingParams.INCOME_FROM_GOODWILL_CREDIT_PENALTY.getValue(),
-            
LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue(),
+            
LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue(),
             LoanProductAccountingParams.EXPENSE_GL_ACCOUNT_ID.getValue(),
             
LoanProductAccountingParams.CHARGE_OFF_REASON_CODE_VALUE_ID.getValue(), 
LoanProductConstants.USE_BORROWER_CYCLE_PARAMETER_NAME,
             
LoanProductConstants.PRINCIPAL_VARIATIONS_FOR_BORROWER_CYCLE_PARAMETER_NAME,
@@ -1992,7 +1992,7 @@ public final class LoanProductDataValidator {
     }
 
     private void validateChargeOffToExpenseMappings(final DataValidatorBuilder 
baseDataValidator, final JsonElement element) {
-        String parameterName = 
LoanProductAccountingParams.CHARGE_OFF_REASONS_TO_EXPENSE.getValue();
+        String parameterName = 
LoanProductAccountingParams.CHARGE_OFF_REASON_TO_EXPENSE_ACCOUNT_MAPPINGS.getValue();
 
         if (this.fromApiJsonHelper.parameterExists(parameterName, element)) {
             final JsonArray chargeOffToExpenseMappingArray = 
this.fromApiJsonHelper.extractJsonArrayNamed(parameterName, element);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java
index c87d674ad..84d518adc 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanProductChargeOffReasonMappingsTest.java
@@ -20,11 +20,6 @@ package org.apache.fineract.integrationtests;
 
 import static 
org.apache.fineract.integrationtests.common.funds.FundsResourceHandler.createFund;
 
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.builder.ResponseSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.specification.RequestSpecification;
-import io.restassured.specification.ResponseSpecification;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -34,84 +29,60 @@ import 
org.apache.fineract.client.models.ChargeToGLAccountMapper;
 import org.apache.fineract.client.models.GetLoanFeeToIncomeAccountMappings;
 import 
org.apache.fineract.client.models.GetLoanPaymentChannelToFundSourceMappings;
 import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
-import org.apache.fineract.client.models.PostChargeOffReasonsToExpenseMappings;
+import 
org.apache.fineract.client.models.PostChargeOffReasonToExpenseAccountMappings;
 import org.apache.fineract.client.models.PostLoanProductsRequest;
 import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
 import org.apache.fineract.client.util.CallFailedRuntimeException;
-import org.apache.fineract.integrationtests.common.BusinessStepHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.apache.fineract.integrationtests.common.accounting.Account;
 import 
org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
 import org.apache.fineract.integrationtests.common.system.CodeHelper;
+import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 public class LoanProductChargeOffReasonMappingsTest extends 
BaseLoanIntegrationTest {
 
     private static final String CODE_VALUE_NAME = "ChargeOffReasons";
-
-    private static ResponseSpecification responseSpec;
-    private static RequestSpecification requestSpec;
-    private static LoanTransactionHelper loanTransactionHelper;
-
-    @BeforeAll
-    public static void setup() {
-        Utils.initializeRESTAssured();
-        requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
-        requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
-        requestSpec.header("Fineract-Platform-TenantId", "default");
-        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
-        loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
-        BusinessStepHelper businessStepHelper = new BusinessStepHelper();
-        // setup COB Business Steps to prevent test failing due other 
integration test configurations
-        businessStepHelper.updateSteps("LOAN_CLOSE_OF_BUSINESS", 
"APPLY_CHARGE_TO_OVERDUE_LOANS", "LOAN_DELINQUENCY_CLASSIFICATION",
-                "CHECK_LOAN_REPAYMENT_DUE", "CHECK_LOAN_REPAYMENT_OVERDUE", 
"UPDATE_LOAN_ARREARS_AGING", "ADD_PERIODIC_ACCRUAL_ENTRIES",
-                "EXTERNAL_ASSET_OWNER_TRANSFER", "ACCRUAL_ACTIVITY_POSTING");
-    }
+    private final Account expenseAccount = 
accountHelper.createExpenseAccount();
+    private final Account otherExpenseAccount = 
accountHelper.createExpenseAccount();
 
     @Test
-    public void testCreateLoanProductWithValidChargeOffReason() {
-        final String creationBusinessDay = "15 January 2023";
-        runAt(creationBusinessDay, () -> {
+    public void testCreateAndUpdateLoanProductWithValidChargeOffReason() {
+        runAt("15 January 2023", () -> {
             Integer chargeOffReasons = createChargeOffReason();
-            Long localLoanProductId = 
loanTransactionHelper.createLoanProduct(loanProductsRequest(Long.valueOf(chargeOffReasons),
 16L))
+            Long localLoanProductId = loanTransactionHelper
+                    
.createLoanProduct(loanProductsRequest(Long.valueOf(chargeOffReasons), 
expenseAccount.getAccountID().longValue()))
                     .getResourceId();
 
             Assertions.assertNotNull(localLoanProductId);
 
-            final GetLoanProductsProductIdResponse loanProduct = 
loanTransactionHelper.getLoanProduct(localLoanProductId.intValue());
-            
Assertions.assertNotNull(loanProduct.getChargeOffReasonToGLAccountMappings());
-            
Assertions.assertFalse(loanProduct.getChargeOffReasonToGLAccountMappings().isEmpty());
-        });
-    }
+            GetLoanProductsProductIdResponse loanProductDetails = 
loanTransactionHelper.getLoanProduct(localLoanProductId.intValue());
+            Assertions.assertEquals(expenseAccount.getAccountID().longValue(),
+                    
loanProductDetails.getChargeOffReasonToExpenseAccountMappings().get(0).getChargeOffExpenseAccount().getId());
+            Assertions.assertEquals(Long.valueOf(chargeOffReasons),
+                    
loanProductDetails.getChargeOffReasonToExpenseAccountMappings().get(0).getChargeOffReasonCodeValue().getId());
 
-    @Test
-    public void testUpdateLoanProductWithValidChargeOffReason() {
-        final String creationBusinessDay = "15 January 2023";
-        runAt(creationBusinessDay, () -> {
-            Integer chargeOffReasons = createChargeOffReason();
-            List<PostChargeOffReasonsToExpenseMappings> 
chargeOffReasonsToExpenseMappings = new ArrayList<>();
-            PostChargeOffReasonsToExpenseMappings 
getChargeOffReasonsToExpenseMappings = new 
PostChargeOffReasonsToExpenseMappings();
-            
getChargeOffReasonsToExpenseMappings.setChargeOffReasonCodeValueId(Long.valueOf(chargeOffReasons));
-            getChargeOffReasonsToExpenseMappings.setExpenseGLAccountId(15L);
-            
chargeOffReasonsToExpenseMappings.add(getChargeOffReasonsToExpenseMappings);
+            List<PostChargeOffReasonToExpenseAccountMappings> 
chargeOffReasonToExpenseAccountMappings = 
createPostChargeOffReasonToExpenseAccountMappings(
+                    Long.valueOf(chargeOffReasons), 
otherExpenseAccount.getAccountID().longValue());
 
-            Long localLoanProductId = 
loanTransactionHelper.updateLoanProduct(1L,
-                    new 
PutLoanProductsProductIdRequest().locale("en").chargeOffReasonsToExpenseMappings(chargeOffReasonsToExpenseMappings))
-                    .getResourceId();
+            loanTransactionHelper.updateLoanProduct(localLoanProductId, new 
PutLoanProductsProductIdRequest().locale("en")
+                    
.chargeOffReasonToExpenseAccountMappings(chargeOffReasonToExpenseAccountMappings)).getResourceId();
 
-            Assertions.assertNotNull(localLoanProductId);
+            loanProductDetails = 
loanTransactionHelper.getLoanProduct(localLoanProductId.intValue());
+            
Assertions.assertEquals(otherExpenseAccount.getAccountID().longValue(),
+                    
loanProductDetails.getChargeOffReasonToExpenseAccountMappings().get(0).getChargeOffExpenseAccount().getId());
+            Assertions.assertEquals(Long.valueOf(chargeOffReasons),
+                    
loanProductDetails.getChargeOffReasonToExpenseAccountMappings().get(0).getChargeOffReasonCodeValue().getId());
         });
     }
 
     @Test
     public void testCreateLoanProductWithInvalidGLAccount() {
-        final String creationBusinessDay = "15 January 2023";
-        runAt(creationBusinessDay, () -> {
+        runAt("15 January 2023", () -> {
             try {
                 Integer chargeOffReasons = createChargeOffReason();
-                
loanTransactionHelper.createLoanProduct(loanProductsRequest(Long.valueOf(chargeOffReasons),
 9999L));
+                
loanTransactionHelper.createLoanProduct(loanProductsRequest(Long.valueOf(chargeOffReasons),
 -1L));
             } catch (CallFailedRuntimeException e) {
                 
Assertions.assertTrue(e.getMessage().contains("validation.msg.glaccount.not.found"));
             }
@@ -120,10 +91,9 @@ public class LoanProductChargeOffReasonMappingsTest extends 
BaseLoanIntegrationT
 
     @Test
     public void testCreateLoanProductWithInvalidChargeOffReason() {
-        final String creationBusinessDay = "15 January 2023";
-        runAt(creationBusinessDay, () -> {
+        runAt("15 January 2023", () -> {
             try {
-                
loanTransactionHelper.createLoanProduct(loanProductsRequest(1L, 12L));
+                
loanTransactionHelper.createLoanProduct(loanProductsRequest(-1L, 
expenseAccount.getAccountID().longValue()));
             } catch (CallFailedRuntimeException e) {
                 
Assertions.assertTrue(e.getMessage().contains("validation.msg.chargeoffreason.invalid"));
             }
@@ -141,11 +111,8 @@ public class LoanProductChargeOffReasonMappingsTest 
extends BaseLoanIntegrationT
         List<ChargeToGLAccountMapper> penaltyToIncomeAccountMappings = new 
ArrayList<>();
         List<GetLoanFeeToIncomeAccountMappings> feeToIncomeAccountMappings = 
new ArrayList<>();
 
-        List<PostChargeOffReasonsToExpenseMappings> 
chargeOffReasonsToExpenseMappings = new ArrayList<>();
-        PostChargeOffReasonsToExpenseMappings 
getChargeOffReasonsToExpenseMappings = new 
PostChargeOffReasonsToExpenseMappings();
-        
getChargeOffReasonsToExpenseMappings.setChargeOffReasonCodeValueId(chargeOffReasonId);
-        
getChargeOffReasonsToExpenseMappings.setExpenseGLAccountId(glAccountId);
-        
chargeOffReasonsToExpenseMappings.add(getChargeOffReasonsToExpenseMappings);
+        List<PostChargeOffReasonToExpenseAccountMappings> 
chargeOffReasonToExpenseAccountMappings = 
createPostChargeOffReasonToExpenseAccountMappings(
+                chargeOffReasonId, glAccountId);
 
         List<GetLoanPaymentChannelToFundSourceMappings> 
paymentChannelToFundSourceMappings = new ArrayList<>();
         GetLoanPaymentChannelToFundSourceMappings 
loanPaymentChannelToFundSourceMappings = new 
GetLoanPaymentChannelToFundSourceMappings();
@@ -248,7 +215,8 @@ public class LoanProductChargeOffReasonMappingsTest extends 
BaseLoanIntegrationT
                 .delinquencyBucketId(delinquencyBucketId.longValue())//
                 
.paymentChannelToFundSourceMappings(paymentChannelToFundSourceMappings)//
                 
.penaltyToIncomeAccountMappings(penaltyToIncomeAccountMappings)//
-                
.chargeOffReasonsToExpenseMappings(chargeOffReasonsToExpenseMappings).feeToIncomeAccountMappings(feeToIncomeAccountMappings)//
+                
.chargeOffReasonToExpenseAccountMappings(chargeOffReasonToExpenseAccountMappings)
+                .feeToIncomeAccountMappings(feeToIncomeAccountMappings)//
                 .isInterestRecalculationEnabled(true)//
                 .preClosureInterestCalculationStrategy(1)//
                 .rescheduleStrategyMethod(3)//
@@ -258,6 +226,17 @@ public class LoanProductChargeOffReasonMappingsTest 
extends BaseLoanIntegrationT
                 .allowPartialPeriodInterestCalcualtion(false);//
     }
 
+    @NotNull
+    private static List<PostChargeOffReasonToExpenseAccountMappings> 
createPostChargeOffReasonToExpenseAccountMappings(
+            Long chargeOffReasonId, Long glAccountId) {
+        List<PostChargeOffReasonToExpenseAccountMappings> 
chargeOffReasonToExpenseAccountMappings = new ArrayList<>();
+        PostChargeOffReasonToExpenseAccountMappings 
chargeOffReasonToExpenseAccountMapping = new 
PostChargeOffReasonToExpenseAccountMappings();
+        
chargeOffReasonToExpenseAccountMapping.setChargeOffReasonCodeValueId(chargeOffReasonId);
+        
chargeOffReasonToExpenseAccountMapping.setExpenseGLAccountId(glAccountId);
+        
chargeOffReasonToExpenseAccountMappings.add(chargeOffReasonToExpenseAccountMapping);
+        return chargeOffReasonToExpenseAccountMappings;
+    }
+
     private Integer createChargeOffReason() {
         Integer chargeOffReasonId;
         HashMap<String, Object> codes = CodeHelper.getCodeByName(requestSpec, 
responseSpec, CODE_VALUE_NAME);
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 eded00f83..40116b9ae 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
@@ -108,7 +108,7 @@ public class LoanProductTestBuilder {
 
     private List<Map<String, Long>> feeToIncomeAccountMappings = null;
     private List<Map<String, Long>> penaltyToIncomeAccountMappings = null;
-    private List<Map<String, Long>> chargeOffReasonsToExpenseMappings = null;
+    private List<Map<String, Long>> chargeOffReasonToExpenseAccountMappings = 
null;
     private Account feeAndPenaltyAssetAccount;
 
     private Boolean multiDisburseLoan = false;
@@ -306,8 +306,8 @@ public class LoanProductTestBuilder {
             map.put("penaltyToIncomeAccountMappings", 
this.penaltyToIncomeAccountMappings);
         }
 
-        if (this.chargeOffReasonsToExpenseMappings != null) {
-            map.put("chargeOffReasonsToExpenseMappings", 
this.chargeOffReasonsToExpenseMappings);
+        if (this.chargeOffReasonToExpenseAccountMappings != null) {
+            map.put("chargeOffReasonToExpenseAccountMappings", 
this.chargeOffReasonToExpenseAccountMappings);
         }
 
         if (this.dueDaysForRepaymentEvent != null) {
@@ -812,14 +812,14 @@ public class LoanProductTestBuilder {
         return this;
     }
 
-    public LoanProductTestBuilder withChargeOffReasonsToExpenseMappings(final 
Long reasonId, final Long accountId) {
-        if (this.chargeOffReasonsToExpenseMappings == null) {
-            this.chargeOffReasonsToExpenseMappings = new ArrayList<>();
+    public LoanProductTestBuilder 
withchargeOffReasonToExpenseAccountMappings(final Long reasonId, final Long 
accountId) {
+        if (this.chargeOffReasonToExpenseAccountMappings == null) {
+            this.chargeOffReasonToExpenseAccountMappings = new ArrayList<>();
         }
         Map<String, Long> newMap = new HashMap<>();
         newMap.put("chargeOffReasonCodeValueId", reasonId);
         newMap.put("expenseGLAccountId", accountId);
-        this.chargeOffReasonsToExpenseMappings.add(newMap);
+        this.chargeOffReasonToExpenseAccountMappings.add(newMap);
         return this;
     }
 

Reply via email to