FINERACT-2040: CBR journal entries when loan is charged off fix
This commit is contained in:
+27
-2
@@ -1074,10 +1074,17 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
|
|
||||||
private void createJournalEntriesForCreditBalanceRefund(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
private void createJournalEntriesForCreditBalanceRefund(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
||||||
final Office office) {
|
final Office office) {
|
||||||
|
final boolean isMarkedChargeOff = loanDTO.isMarkedAsChargeOff();
|
||||||
|
createJournalEntriesForLoanCreditBalanceRefund(loanDTO, loanTransactionDTO, office, isMarkedChargeOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createJournalEntriesForLoanCreditBalanceRefund(final LoanDTO loanDTO, final LoanTransactionDTO loanTransactionDTO,
|
||||||
|
final Office office, boolean isMarkedChargeOff) {
|
||||||
// loan properties
|
// loan properties
|
||||||
final Long loanProductId = loanDTO.getLoanProductId();
|
final Long loanProductId = loanDTO.getLoanProductId();
|
||||||
final Long loanId = loanDTO.getLoanId();
|
final Long loanId = loanDTO.getLoanId();
|
||||||
final String currencyCode = loanDTO.getCurrencyCode();
|
final String currencyCode = loanDTO.getCurrencyCode();
|
||||||
|
final boolean isMarkedFraud = loanDTO.isMarkedAsFraud();
|
||||||
|
|
||||||
// transaction properties
|
// transaction properties
|
||||||
final String transactionId = loanTransactionDTO.getTransactionId();
|
final String transactionId = loanTransactionDTO.getTransactionId();
|
||||||
@@ -1093,11 +1100,13 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
|
|
||||||
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
|
if (principalAmount != null && principalAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
totalAmount = totalAmount.add(principalAmount);
|
totalAmount = totalAmount.add(principalAmount);
|
||||||
journalAmountHolders.add(new JournalAmountHolder(AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), principalAmount));
|
journalAmountHolders
|
||||||
|
.add(new JournalAmountHolder(determineAccrualAccount(isMarkedChargeOff, isMarkedFraud, false), principalAmount));
|
||||||
}
|
}
|
||||||
if (overpaymentAmount != null && overpaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
|
if (overpaymentAmount != null && overpaymentAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
totalAmount = totalAmount.add(overpaymentAmount);
|
totalAmount = totalAmount.add(overpaymentAmount);
|
||||||
journalAmountHolders.add(new JournalAmountHolder(AccrualAccountsForLoan.OVERPAYMENT.getValue(), overpaymentAmount));
|
journalAmountHolders
|
||||||
|
.add(new JournalAmountHolder(determineAccrualAccount(isMarkedChargeOff, isMarkedFraud, true), overpaymentAmount));
|
||||||
}
|
}
|
||||||
|
|
||||||
JournalAmountHolder totalAmountHolder = new JournalAmountHolder(AccrualAccountsForLoan.FUND_SOURCE.getValue(), totalAmount);
|
JournalAmountHolder totalAmountHolder = new JournalAmountHolder(AccrualAccountsForLoan.FUND_SOURCE.getValue(), totalAmount);
|
||||||
@@ -1106,6 +1115,22 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer determineAccrualAccount(boolean isMarkedChargeOff, boolean isMarkedFraud, boolean isOverpayment) {
|
||||||
|
if (isMarkedChargeOff) {
|
||||||
|
if (isMarkedFraud) {
|
||||||
|
return AccrualAccountsForLoan.CHARGE_OFF_FRAUD_EXPENSE.getValue();
|
||||||
|
} else {
|
||||||
|
return AccrualAccountsForLoan.CHARGE_OFF_EXPENSE.getValue();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isOverpayment) {
|
||||||
|
return AccrualAccountsForLoan.OVERPAYMENT.getValue();
|
||||||
|
} else {
|
||||||
|
return AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
|
private void createJournalEntriesForRefundForActiveLoan(LoanDTO loanDTO, LoanTransactionDTO loanTransactionDTO, Office office) {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
// loan properties
|
// loan properties
|
||||||
|
|||||||
+98
@@ -19,6 +19,7 @@
|
|||||||
package org.apache.fineract.integrationtests;
|
package org.apache.fineract.integrationtests;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import io.restassured.builder.RequestSpecBuilder;
|
import io.restassured.builder.RequestSpecBuilder;
|
||||||
@@ -29,6 +30,7 @@ import io.restassured.specification.ResponseSpecification;
|
|||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -43,11 +45,15 @@ import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
|||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
||||||
import org.apache.fineract.integrationtests.common.Utils;
|
import org.apache.fineract.integrationtests.common.Utils;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.accounting.JournalEntryHelper;
|
||||||
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||||
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
|
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
|
||||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||||
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
|
||||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||||
|
import org.apache.fineract.integrationtests.common.system.CodeHelper;
|
||||||
import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
|
import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -64,6 +70,8 @@ public class LoanTransactionReverseReplayTest {
|
|||||||
private ClientHelper clientHelper;
|
private ClientHelper clientHelper;
|
||||||
private LoanTransactionHelper loanTransactionHelper;
|
private LoanTransactionHelper loanTransactionHelper;
|
||||||
private InlineLoanCOBHelper inlineLoanCOBHelper;
|
private InlineLoanCOBHelper inlineLoanCOBHelper;
|
||||||
|
private JournalEntryHelper journalEntryHelper;
|
||||||
|
private AccountHelper accountHelper;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
@@ -75,6 +83,8 @@ public class LoanTransactionReverseReplayTest {
|
|||||||
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||||
clientHelper = new ClientHelper(requestSpec, responseSpec);
|
clientHelper = new ClientHelper(requestSpec, responseSpec);
|
||||||
inlineLoanCOBHelper = new InlineLoanCOBHelper(requestSpec, responseSpec);
|
inlineLoanCOBHelper = new InlineLoanCOBHelper(requestSpec, responseSpec);
|
||||||
|
journalEntryHelper = new JournalEntryHelper(requestSpec, responseSpec);
|
||||||
|
accountHelper = new AccountHelper(requestSpec, responseSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,6 +229,94 @@ public class LoanTransactionReverseReplayTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loanTransactionReverseReplayWithChargeOffAndCBR() {
|
||||||
|
try {
|
||||||
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||||
|
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||||
|
.date("04 October 2022").dateFormat(DATE_PATTERN).locale("en"));
|
||||||
|
|
||||||
|
final Account assetAccount = accountHelper.createAssetAccount();
|
||||||
|
final Account assetFeeAndPenaltyAccount = accountHelper.createAssetAccount();
|
||||||
|
final Account incomeAccount = accountHelper.createIncomeAccount();
|
||||||
|
final Account expenseAccount = accountHelper.createExpenseAccount();
|
||||||
|
final Account overpaymentAccount = accountHelper.createLiabilityAccount();
|
||||||
|
|
||||||
|
// Loan ExternalId
|
||||||
|
String loanExternalIdStr = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
// Client and Loan account creation
|
||||||
|
|
||||||
|
final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
|
||||||
|
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
|
||||||
|
.withRepaymentAfterEvery("1").withNumberOfRepayments("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
|
||||||
|
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsFlat()
|
||||||
|
.withAccountingRulePeriodicAccrual(new Account[] { assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
|
||||||
|
.withDaysInMonth("30").withDaysInYear("365").withMoratorium("0", "0")
|
||||||
|
.withFeeAndPenaltyAssetAccount(assetFeeAndPenaltyAccount).build(null);
|
||||||
|
final Integer loanProductID = loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
|
|
||||||
|
final Integer loanId = createLoanAccount(clientId, loanProductID.longValue(), loanExternalIdStr);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
PostLoansLoanIdTransactionsResponse chargeOffResponse = loanTransactionHelper.chargeOffLoan(loanId.longValue(),
|
||||||
|
new PostLoansLoanIdTransactionsRequest().transactionDate("03 October 2022").locale("en").dateFormat("dd MMMM yyyy")
|
||||||
|
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
|
||||||
|
|
||||||
|
final PostLoansLoanIdTransactionsResponse repaymentTransaction = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().dateFormat(DATE_PATTERN).transactionDate("03 October 2022").locale("en")
|
||||||
|
.transactionAmount(1500.0));
|
||||||
|
|
||||||
|
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
|
||||||
|
|
||||||
|
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||||
|
.date("05 October 2022").dateFormat(DATE_PATTERN).locale("en"));
|
||||||
|
|
||||||
|
PostLoansLoanIdTransactionsResponse cbrTransactionResponse = loanTransactionHelper.makeCreditBalanceRefund(loanExternalIdStr,
|
||||||
|
new PostLoansLoanIdTransactionsRequest().dateFormat(DATE_PATTERN).transactionDate("05 October 2022").locale("en")
|
||||||
|
.transactionAmount(500.0));
|
||||||
|
|
||||||
|
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||||
|
.date("06 October 2022").dateFormat(DATE_PATTERN).locale("en"));
|
||||||
|
|
||||||
|
loanTransactionHelper.reverseLoanTransaction(loanExternalIdStr, repaymentTransaction.getResourceId(),
|
||||||
|
new PostLoansLoanIdTransactionsTransactionIdRequest().transactionDate("06 October 2022").locale("en")
|
||||||
|
.dateFormat(DATE_PATTERN).transactionAmount(0.0));
|
||||||
|
|
||||||
|
inlineLoanCOBHelper.executeInlineCOB(List.of(loanId.longValue()));
|
||||||
|
GetLoansLoanIdResponse loansLoanIdResponse = loanTransactionHelper.getLoanDetails(loanExternalIdStr);
|
||||||
|
int lastTransactionIndex = loansLoanIdResponse.getTransactions().size() - 1;
|
||||||
|
assertEquals(500.0, loansLoanIdResponse.getTransactions().get(lastTransactionIndex).getAmount());
|
||||||
|
|
||||||
|
ArrayList<HashMap> journalEntriesForCBR = journalEntryHelper
|
||||||
|
.getJournalEntriesByTransactionId("L" + cbrTransactionResponse.getResourceId().toString());
|
||||||
|
ArrayList<HashMap> journalEntriesForChargeOff = journalEntryHelper
|
||||||
|
.getJournalEntriesByTransactionId("L" + chargeOffResponse.getResourceId().toString());
|
||||||
|
assertNotNull(journalEntriesForCBR);
|
||||||
|
assertNotNull(journalEntriesForChargeOff);
|
||||||
|
|
||||||
|
String expenseGlAccountCode = (String) journalEntriesForChargeOff.get(0).get("glAccountCode");
|
||||||
|
String assetGlAccountCode = (String) journalEntriesForChargeOff.get(1).get("glAccountCode");
|
||||||
|
|
||||||
|
List<HashMap> cbrExpenseJournalEntries = journalEntriesForCBR.stream() //
|
||||||
|
.filter(journalEntry -> expenseGlAccountCode.equals(journalEntry.get("glAccountCode"))) //
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
List<HashMap> cbrAssetJournalEntries = journalEntriesForCBR.stream() //
|
||||||
|
.filter(journalEntry -> assetGlAccountCode.equals(journalEntry.get("glAccountCode"))) //
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
assertEquals(2, cbrExpenseJournalEntries.size());
|
||||||
|
assertEquals(2, cbrAssetJournalEntries.size());
|
||||||
|
} finally {
|
||||||
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper) {
|
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper) {
|
||||||
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, null);
|
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, null);
|
||||||
final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
|
final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
|
||||||
|
|||||||
Reference in New Issue
Block a user