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();