FINERACT-1806: Fix mapping to handle multiple loan product configuration

This commit is contained in:
Adam Saghy
2025-01-08 15:12:54 +01:00
parent afc09b5333
commit d2afd1aac7
5 changed files with 525 additions and 465 deletions
@@ -66,8 +66,9 @@ public interface ProductToGLAccountMappingRepository
List<ProductToGLAccountMapping> findAllChargeOffReasonsMappings(@Param("productId") Long productId, List<ProductToGLAccountMapping> findAllChargeOffReasonsMappings(@Param("productId") Long productId,
@Param("productType") int productType); @Param("productType") int productType);
@Query("select mapping from ProductToGLAccountMapping mapping where mapping.chargeOffReason.id =:chargeOffReasonId") @Query("select mapping from ProductToGLAccountMapping mapping where mapping.chargeOffReason.id =:chargeOffReasonId AND mapping.productId =:productId AND mapping.productType =:productType")
ProductToGLAccountMapping findChargeOffReasonMappingById(@Param("chargeOffReasonId") Long chargeOffReasonId); ProductToGLAccountMapping findChargeOffReasonMapping(@Param("productId") Long productId, @Param("productType") Integer productType,
@Param("chargeOffReasonId") Long chargeOffReasonId);
@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") @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> findAllRegularMappings(@Param("productId") Long productId, @Param("productType") Integer productType);
@@ -177,8 +177,9 @@ public class AccountingProcessorHelper {
chargeOffReasonCodeValue); chargeOffReasonCodeValue);
} }
public ProductToGLAccountMapping getChargeOffMappingByCodeValue(Long chargeOffReasonCodeValue) { public ProductToGLAccountMapping getChargeOffMappingByCodeValue(Long loanProductId, PortfolioProductType productType,
return accountMappingRepository.findChargeOffReasonMappingById(chargeOffReasonCodeValue); Long chargeOffReasonId) {
return accountMappingRepository.findChargeOffReasonMapping(loanProductId, productType.getValue(), chargeOffReasonId);
} }
public SavingsDTO populateSavingsDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled, public SavingsDTO populateSavingsDtoFromMap(final Map<String, Object> accountingBridgeData, final boolean cashBasedAccountingEnabled,
@@ -37,6 +37,7 @@ import org.apache.fineract.accounting.journalentry.data.LoanTransactionDTO;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.PortfolioProductType;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -233,7 +234,7 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
Long chargeOffReasonCodeValue = loanDTO.getChargeOffReasonCodeValue(); Long chargeOffReasonCodeValue = loanDTO.getChargeOffReasonCodeValue();
ProductToGLAccountMapping mapping = chargeOffReasonCodeValue != null ProductToGLAccountMapping mapping = chargeOffReasonCodeValue != null
? helper.getChargeOffMappingByCodeValue(chargeOffReasonCodeValue) ? helper.getChargeOffMappingByCodeValue(loanProductId, PortfolioProductType.LOAN, chargeOffReasonCodeValue)
: null; : null;
if (mapping != null) { if (mapping != null) {
GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId, GLAccount accountCredit = this.helper.getLinkedGLAccountForLoanProduct(loanProductId,
@@ -37,6 +37,7 @@ import org.apache.fineract.accounting.journalentry.service.AccountingProcessorHe
import org.apache.fineract.accounting.journalentry.service.AccrualBasedAccountingProcessorForLoan; import org.apache.fineract.accounting.journalentry.service.AccrualBasedAccountingProcessorForLoan;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.PortfolioProductType;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData; import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -48,7 +49,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class CreateJournalEntriesForChargeOffLoanTest { class CreateJournalEntriesForChargeOffLoanTest {
private static final Long chargeOffReasons = 15L; private static final Long chargeOffReasonId = 15L;
@Mock @Mock
private AccountingProcessorHelper helper; private AccountingProcessorHelper helper;
@@ -71,7 +72,7 @@ class CreateJournalEntriesForChargeOffLoanTest {
transactionType, new BigDecimal("500.00"), new BigDecimal("500.00"), null, null, null, null, false, Collections.emptyList(), transactionType, new BigDecimal("500.00"), new BigDecimal("500.00"), null, null, null, null, false, Collections.emptyList(),
Collections.emptyList(), false, "", null, null, null, null); Collections.emptyList(), false, "", null, null, null, null);
loanDTO = new LoanDTO(1L, 1L, 1L, "USD", false, true, true, List.of(loanTransactionDTO), false, false, chargeOffReasons); loanDTO = new LoanDTO(1L, 1L, 1L, "USD", false, true, true, List.of(loanTransactionDTO), false, false, chargeOffReasonId);
} }
@Test @Test
@@ -84,7 +85,7 @@ class CreateJournalEntriesForChargeOffLoanTest {
ProductToGLAccountMapping chargeToGLAccountMapper = new ProductToGLAccountMapping(); ProductToGLAccountMapping chargeToGLAccountMapper = new ProductToGLAccountMapping();
chargeToGLAccountMapper.setGlAccount(chargeOffGLAccount); chargeToGLAccountMapper.setGlAccount(chargeOffGLAccount);
when(helper.getChargeOffMappingByCodeValue(chargeOffReasons)).thenReturn(chargeToGLAccountMapper); when(helper.getChargeOffMappingByCodeValue(1L, PortfolioProductType.LOAN, chargeOffReasonId)).thenReturn(chargeToGLAccountMapper);
GLAccount loanPortfolioGLAccount = new GLAccount(); GLAccount loanPortfolioGLAccount = new GLAccount();
loanPortfolioGLAccount.setId(20L); loanPortfolioGLAccount.setId(20L);
@@ -96,7 +97,7 @@ class CreateJournalEntriesForChargeOffLoanTest {
processor.createJournalEntriesForLoan(loanDTO); processor.createJournalEntriesForLoan(loanDTO);
verify(helper, times(1)).getChargeOffMappingByCodeValue(chargeOffReasons); verify(helper, times(1)).getChargeOffMappingByCodeValue(1L, PortfolioProductType.LOAN, chargeOffReasonId);
verify(helper, times(1)).getLinkedGLAccountForLoanProduct(1L, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), 1L); verify(helper, times(1)).getLinkedGLAccountForLoanProduct(1L, AccrualAccountsForLoan.LOAN_PORTFOLIO.getValue(), 1L);
verify(helper, times(1)).createCreditJournalEntryOrReversalForLoan(helper.getOfficeById(1L), "USD", verify(helper, times(1)).createCreditJournalEntryOrReversalForLoan(helper.getOfficeById(1L), "USD",
AccrualAccountsForLoan.LOAN_PORTFOLIO, 1L, null, 1L, "txn-123", LocalDate.now(ZoneId.systemDefault()), AccrualAccountsForLoan.LOAN_PORTFOLIO, 1L, null, 1L, "txn-123", LocalDate.now(ZoneId.systemDefault()),
@@ -110,7 +111,7 @@ class CreateJournalEntriesForChargeOffLoanTest {
void shouldCreateJournalEntriesForChargeOffWithFraud() { void shouldCreateJournalEntriesForChargeOffWithFraud() {
loanDTO.setMarkedAsFraud(true); loanDTO.setMarkedAsFraud(true);
when(helper.getChargeOffMappingByCodeValue(chargeOffReasons)).thenReturn(null); when(helper.getChargeOffMappingByCodeValue(1L, PortfolioProductType.LOAN, chargeOffReasonId)).thenReturn(null);
GLAccount loanPortfolioGLAccount = new GLAccount(); GLAccount loanPortfolioGLAccount = new GLAccount();
loanPortfolioGLAccount.setId(20L); loanPortfolioGLAccount.setId(20L);
@@ -138,7 +139,7 @@ class CreateJournalEntriesForChargeOffLoanTest {
void shouldCreateJournalEntriesForChargeOffWithoutFraud() { void shouldCreateJournalEntriesForChargeOffWithoutFraud() {
loanDTO.setMarkedAsFraud(false); loanDTO.setMarkedAsFraud(false);
when(helper.getChargeOffMappingByCodeValue(chargeOffReasons)).thenReturn(null); when(helper.getChargeOffMappingByCodeValue(1L, PortfolioProductType.LOAN, chargeOffReasonId)).thenReturn(null);
GLAccount loanPortfolioGLAccount = new GLAccount(); GLAccount loanPortfolioGLAccount = new GLAccount();
loanPortfolioGLAccount.setId(20L); loanPortfolioGLAccount.setId(20L);
@@ -64,6 +64,7 @@ import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.apache.fineract.integrationtests.common.system.CodeHelper; import org.apache.fineract.integrationtests.common.system.CodeHelper;
import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper; import org.apache.fineract.integrationtests.inlinecob.InlineLoanCOBHelper;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -104,6 +105,8 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
@Test @Test
public void loanChargeOffAccountingTreatmentTestForPeriodicAccrualAccounting() { public void loanChargeOffAccountingTreatmentTestForPeriodicAccrualAccounting() {
runAt("12 September 2022", () -> {
// Loan ExternalId // Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
@@ -138,11 +141,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
// set loan as chargeoff // set loan as chargeoff
String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6) + Utils.randomStringGenerator("is", 5); String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6)
+ Utils.randomStringGenerator("is", 5);
Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1); Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
String transactionExternalId = UUID.randomUUID().toString(); String transactionExternalId = UUID.randomUUID().toString();
this.loanTransactionHelper.chargeOffLoan((long) loanId, new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022") this.loanTransactionHelper.chargeOffLoan((long) loanId,
.locale("en").dateFormat("dd MMMM yyyy").externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId)); new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022").locale("en").dateFormat("dd MMMM yyyy")
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetails.getStatus().getActive()); assertTrue(loanDetails.getStatus().getActive());
@@ -245,18 +250,19 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetails.getStatus().getOverpaid()); assertTrue(loanDetails.getStatus().getOverpaid());
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
String transactionDate = Utils.dateFormatter.format(todaysDate);
// verify Journal entries for Charge Adjustment // verify Journal entries for Charge Adjustment
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount, transactionDate, this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount, "12 September 2022",
new JournalEntry(10, JournalEntry.TransactionType.CREDIT)); new JournalEntry(10, JournalEntry.TransactionType.CREDIT));
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, transactionDate, this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "12 September 2022",
new JournalEntry(10, JournalEntry.TransactionType.DEBIT)); new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
});
} }
@Test @Test
public void loanChargeOffFraudAccountingTreatmentTestForCashBasedAccounting() { public void loanChargeOffFraudAccountingTreatmentTestForCashBasedAccounting() {
runAt("12 September 2022", () -> {
// Loan ExternalId // Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
@@ -301,11 +307,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
assertTrue(loanDetails.getFraud()); assertTrue(loanDetails.getFraud());
// set loan as chargeoff // set loan as chargeoff
String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6) + Utils.randomStringGenerator("is", 5); String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6)
+ Utils.randomStringGenerator("is", 5);
Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1); Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
String transactionExternalId = UUID.randomUUID().toString(); String transactionExternalId = UUID.randomUUID().toString();
this.loanTransactionHelper.chargeOffLoan((long) loanId, new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022") this.loanTransactionHelper.chargeOffLoan((long) loanId,
.locale("en").dateFormat("dd MMMM yyyy").externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId)); new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022").locale("en").dateFormat("dd MMMM yyyy")
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetails.getStatus().getActive()); assertTrue(loanDetails.getStatus().getActive());
@@ -416,19 +424,19 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetails.getStatus().getOverpaid()); assertTrue(loanDetails.getStatus().getOverpaid());
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
String transactionDate = Utils.dateFormatter.format(todaysDate);
// verify Journal entries for Charge Adjustment // verify Journal entries for Charge Adjustment
this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount, transactionDate, this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount, "12 September 2022",
new JournalEntry(10, JournalEntry.TransactionType.CREDIT)); new JournalEntry(10, JournalEntry.TransactionType.CREDIT));
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, transactionDate, this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "12 September 2022",
new JournalEntry(10, JournalEntry.TransactionType.DEBIT)); new JournalEntry(10, JournalEntry.TransactionType.DEBIT));
});
} }
// Tests for Goodwill Credit accounting changes // Tests for Goodwill Credit accounting changes
@Test @Test
public void loanAccountingTreatmentTestForGoodwillCreditPeriodicAccrualAccounting_NoChargeOff() { public void loanAccountingTreatmentTestForGoodwillCreditPeriodicAccrualAccounting_NoChargeOff() {
runAt("12 September 2022", () -> {
// Loan ExternalId // Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
@@ -480,11 +488,14 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
new JournalEntry(780, JournalEntry.TransactionType.DEBIT)); new JournalEntry(780, JournalEntry.TransactionType.DEBIT));
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 September 2022", this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 September 2022",
new JournalEntry(20, JournalEntry.TransactionType.DEBIT)); new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
});
} }
@Test @Test
public void loanAccountingTreatmentTestForGoodwillCreditPeriodicAccrualAccounting_ChargeOff() { public void loanAccountingTreatmentTestForGoodwillCreditPeriodicAccrualAccounting_ChargeOff() {
runAt("12 September 2022", () -> {
// Loan ExternalId // Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
@@ -519,11 +530,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10")); LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "10"));
// set loan as chargeoff // set loan as chargeoff
String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6) + Utils.randomStringGenerator("is", 5); String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6)
+ Utils.randomStringGenerator("is", 5);
Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1); Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
String transactionExternalId = UUID.randomUUID().toString(); String transactionExternalId = UUID.randomUUID().toString();
this.loanTransactionHelper.chargeOffLoan((long) loanId, new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022") this.loanTransactionHelper.chargeOffLoan((long) loanId,
.locale("en").dateFormat("dd MMMM yyyy").externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId)); new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022").locale("en").dateFormat("dd MMMM yyyy")
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetails.getStatus().getActive()); assertTrue(loanDetails.getStatus().getActive());
@@ -553,11 +566,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
new JournalEntry(20, JournalEntry.TransactionType.DEBIT)); new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "8 September 2022", this.journalEntryHelper.checkJournalEntryForExpenseAccount(expenseAccount, "8 September 2022",
new JournalEntry(780, JournalEntry.TransactionType.DEBIT)); new JournalEntry(780, JournalEntry.TransactionType.DEBIT));
});
} }
@Test @Test
public void loanAccountingTreatmentTestForCashBasedAccounting_NoChargeOff() { public void loanAccountingTreatmentTestForCashBasedAccounting_NoChargeOff() {
runAt("12 September 2022", () -> {
// Loan ExternalId // Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
@@ -613,11 +628,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
new JournalEntry(780, JournalEntry.TransactionType.DEBIT)); new JournalEntry(780, JournalEntry.TransactionType.DEBIT));
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 September 2022", this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 September 2022",
new JournalEntry(20, JournalEntry.TransactionType.DEBIT)); new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
});
} }
@Test @Test
public void loanAccountingTreatmentTestForCashBasedAccounting_ChargeOff() { public void loanAccountingTreatmentTestForCashBasedAccounting_ChargeOff() {
runAt("12 September 2022", () -> {
// Loan ExternalId // Loan ExternalId
String loanExternalIdStr = UUID.randomUUID().toString(); String loanExternalIdStr = UUID.randomUUID().toString();
@@ -655,11 +672,13 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
assertTrue(loanDetails.getStatus().getActive()); assertTrue(loanDetails.getStatus().getActive());
// set loan as chargeoff // set loan as chargeoff
String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6) + Utils.randomStringGenerator("is", 5); String randomText = Utils.randomStringGenerator("en", 5) + Utils.randomNumberGenerator(6)
+ Utils.randomStringGenerator("is", 5);
Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1); Integer chargeOffReasonId = CodeHelper.createChargeOffCodeValue(requestSpec, responseSpec, randomText, 1);
String transactionExternalId = UUID.randomUUID().toString(); String transactionExternalId = UUID.randomUUID().toString();
this.loanTransactionHelper.chargeOffLoan((long) loanId, new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022") this.loanTransactionHelper.chargeOffLoan((long) loanId,
.locale("en").dateFormat("dd MMMM yyyy").externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId)); new PostLoansLoanIdTransactionsRequest().transactionDate("6 September 2022").locale("en").dateFormat("dd MMMM yyyy")
.externalId(transactionExternalId).chargeOffReasonId((long) chargeOffReasonId));
loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId);
assertTrue(loanDetails.getStatus().getActive()); assertTrue(loanDetails.getStatus().getActive());
@@ -693,6 +712,7 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
new JournalEntry(1000, JournalEntry.TransactionType.DEBIT)); new JournalEntry(1000, JournalEntry.TransactionType.DEBIT));
this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 September 2022", this.journalEntryHelper.checkJournalEntryForIncomeAccount(incomeAccount, "6 September 2022",
new JournalEntry(20, JournalEntry.TransactionType.DEBIT)); new JournalEntry(20, JournalEntry.TransactionType.DEBIT));
});
} }
@Test @Test
@@ -810,6 +830,10 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsResponse productsResponse = createLoanProductWithAdvancedChargeOffAccounting(chargeOffReason, PostLoanProductsResponse productsResponse = createLoanProductWithAdvancedChargeOffAccounting(chargeOffReason,
chargeOffDelinquentExpenseAccount); chargeOffDelinquentExpenseAccount);
// We are creating a 2nd product to test, the mapping is correct!
PostLoanProductsResponse secondProduct = createLoanProductWithAdvancedChargeOffAccounting(chargeOffReason,
chargeOffDelinquentExpenseAccount);
Assertions.assertNotNull(secondProduct.getResourceId());
// Apply and Approve Loan // Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, productsResponse.getResourceId(), "01 January 2023", 1000.0, 1); Long loanId = applyAndApproveLoan(clientId, productsResponse.getResourceId(), "01 January 2023", 1000.0, 1);
// Disburse Loan // Disburse Loan
@@ -824,6 +848,38 @@ public class LoanChargeOffAccountingTest extends BaseLoanIntegrationTest {
}); });
} }
@Test
public void advancedAccountingForChargeOffFallbackToRegular() {
runAt("02 January 2023", () -> {
final Account chargeOffDelinquentExpenseAccount = accountHelper
.createExpenseAccount("delinquent_expense_for_charge_off_reason");
GetCodesResponse chargeOffReasonCode = fetchChargeOffReasonCode();
PostCodeValueDataResponse chargeOffReason = codeHelper.createCodeValue(chargeOffReasonCode.getId(),
new PostCodeValuesDataRequest().name(Utils.uniqueRandomStringGenerator("DELINQUENT_", 6)).isActive(true).position(10));
GetCodesResponse secondChargeOffReason = fetchChargeOffReasonCode();
PostCodeValueDataResponse secondChargeOffReasonResponse = codeHelper.createCodeValue(secondChargeOffReason.getId(),
new PostCodeValuesDataRequest().name(Utils.uniqueRandomStringGenerator("FRAUD_", 6)).isActive(true).position(10));
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
PostLoanProductsResponse productsResponse = createLoanProductWithAdvancedChargeOffAccounting(chargeOffReason,
chargeOffDelinquentExpenseAccount);
// We are creating a 2nd product to test, the mapping is correct!
PostLoanProductsResponse secondProduct = createLoanProductWithAdvancedChargeOffAccounting(chargeOffReason,
chargeOffDelinquentExpenseAccount);
Assertions.assertNotNull(secondProduct.getResourceId());
// Apply and Approve Loan
Long loanId = applyAndApproveLoan(clientId, productsResponse.getResourceId(), "01 January 2023", 1000.0, 1);
// Disburse Loan
disburseLoan(loanId, BigDecimal.valueOf(1000.00), "01 January 2023");
PostLoansLoanIdTransactionsResponse chargeOffTransaction = this.loanTransactionHelper.chargeOffLoan(loanId,
new PostLoansLoanIdTransactionsRequest().transactionDate("02 January 2023").locale("en").dateFormat("dd MMMM yyyy")
.chargeOffReasonId(secondChargeOffReasonResponse.getSubResourceId()));
// verify journal entries
verifyTRJournalEntries(chargeOffTransaction.getResourceId(), journalEntry(1000.0, loansReceivableAccount, "CREDIT"), //
journalEntry(1000.0, chargeOffExpenseAccount, "DEBIT"));
});
}
private PostLoanProductsResponse createLoanProductWithAdvancedChargeOffAccounting(PostCodeValueDataResponse chargeOffReason, private PostLoanProductsResponse createLoanProductWithAdvancedChargeOffAccounting(PostCodeValueDataResponse chargeOffReason,
Account chargeOffDelinquentExpenseAccount) { Account chargeOffDelinquentExpenseAccount) {
return this.loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest() return this.loanTransactionHelper.createLoanProduct(new PostLoanProductsRequest()