FINERACT-2232: Capitalized income and Caplitalized income adjustment in case of loan is overpaid

This commit is contained in:
Jose Alberto Hernandez
2025-06-14 14:04:27 +02:00
committed by Adam Saghy
parent 009d7b71a9
commit 3b5b96a5f4
3 changed files with 239 additions and 3 deletions
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
@@ -693,11 +694,19 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
addRepaymentForLoan(loanId, 50.58, "15 March 2024");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 0.0, 151.59, 0.0, 150.0, 0.15);
loanTransactionHelper.makeCreditBalanceRefund(loanId, new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
.transactionDate("15 March 2024").locale("en").transactionAmount(0.15));
// Validate Loan is Closed
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 0.0, 151.59, 0.0, 150.0, null);
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 1), 49.71, 49.71, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 49.99, 49.99, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2024, 4, 1), 50.30, 50.30, 0.0, 50.43, 0.0);
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
});
runAt("16 March 2024", () -> {
@@ -709,6 +718,223 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest {
verifyTRJournalEntries(capitalizedIncomeTransactionId, journalEntry(50, loansReceivableAccount, "DEBIT"), //
journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT") //
);
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 50.15, 151.59, 50.00, 150.00, null);
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 1), 49.71, 49.71, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 49.99, 49.99, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2024, 4, 1), 100.30, 50.3, 50.00, 50.43, 0.0);
// Validate Loan is Active
assertTrue(loanDetails.getStatus().getActive());
});
}
@Test
public void testLoanCapitalizedIncomeOnLoanOverpaid() {
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
final AtomicReference<Long> capitalizedIncomeTransactionIdRef = new AtomicReference<>();
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
runAt("1 January 2024", () -> {
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
500.0, 7.0, 3, null);
loanIdRef.set(loanId);
disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "1 January 2024", 50.0)
.getResourceId();
capitalizedIncomeTransactionIdRef.set(capitalizedIncomeTransactionId);
});
runAt("1 February 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "1 February 2024");
});
runAt("1 March 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "1 March 2024");
});
runAt("15 March 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "15 March 2024");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 0.0, 151.59, 0.0, 150.0, 0.15);
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 1), 49.71, 49.71, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 49.99, 49.99, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2024, 4, 1), 50.30, 50.30, 0.0, 50.43, 0.0);
// Validate Loan is Overpaid
assertTrue(loanDetails.getStatus().getOverpaid());
});
runAt("16 March 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "16 March 2024", 50.0).getResourceId();
verifyTRJournalEntries(capitalizedIncomeTransactionId, journalEntry(50, loansReceivableAccount, "DEBIT"), //
journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT") //
);
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 50.0, 151.74, 49.85, 150.15, null);
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2024, 2, 1), 49.71, 49.71, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), 49.99, 49.99, 0.0, 0.0, 0.0);
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2024, 4, 1), 100.30, 50.45, 49.85, 50.58, 0.0);
assertTrue(loanDetails.getStatus().getActive());
});
}
@Test
public void testLoanCapitalizedIncomeAdjustmentOnLoanOverpaid() {
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
final AtomicReference<Long> capitalizedIncomeTransactionIdRef = new AtomicReference<>();
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
runAt("1 January 2024", () -> {
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
500.0, 7.0, 3, null);
loanIdRef.set(loanId);
disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "1 January 2024", 50.0)
.getResourceId();
capitalizedIncomeTransactionIdRef.set(capitalizedIncomeTransactionId);
});
runAt("1 February 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "1 February 2024");
});
runAt("1 March 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "1 March 2024");
});
runAt("1 April 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 60.6, "1 April 2024");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
// Validate Loan is Overpaid
assertTrue(loanDetails.getStatus().getOverpaid());
});
runAt("5 April 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
loanTransactionHelper.makeCreditBalanceRefund(loanId, new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN)
.transactionDate("5 April 2024").locale("en").transactionAmount(10.00));
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
// Validate Loan remains Overpaid
assertTrue(loanDetails.getStatus().getOverpaid());
});
runAt("15 April 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
Long capitalizedIncomeAdjustmentTransactionId = loanTransactionHelper
.capitalizedIncomeAdjustment(loanId, capitalizedIncomeTransactionIdRef.get(), "15 April 2024", 15.0).getResourceId();
verifyTRJournalEntries(capitalizedIncomeAdjustmentTransactionId, journalEntry(15, feeIncomeAccount, "DEBIT"), //
journalEntry(15, overpaymentAccount, "CREDIT") //
);
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
// Validate Loan remains Overpaid
assertTrue(loanDetails.getStatus().getOverpaid());
validateLoanSummaryBalances(loanDetails, 0.0, 151.75, 0.0, 150.00, 15.01);
});
}
@Test
public void testLoanCapitalizedIncomeAdjustmentOnLoanClosed() {
final AtomicReference<Long> loanIdRef = new AtomicReference<>();
final AtomicReference<Long> capitalizedIncomeTransactionIdRef = new AtomicReference<>();
final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest());
final PostLoanProductsResponse loanProductsResponse = loanProductHelper
.createLoanProduct(create4IProgressive().enableIncomeCapitalization(true)
.capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT)
.capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION)
.deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue())
.incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue())
.capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE));
runAt("1 January 2024", () -> {
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024",
500.0, 7.0, 3, null);
loanIdRef.set(loanId);
disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024");
Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "1 January 2024", 50.0)
.getResourceId();
capitalizedIncomeTransactionIdRef.set(capitalizedIncomeTransactionId);
});
runAt("1 February 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "1 February 2024");
});
runAt("1 March 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.58, "1 March 2024");
});
runAt("1 April 2024", () -> {
Long loanId = loanIdRef.get();
executeInlineCOB(loanId);
addRepaymentForLoan(loanId, 50.59, "1 April 2024");
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 0.0, 151.75, 0.0, 150.00, null);
// Validate Loan goes to Closed
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
});
runAt("15 April 2024", () -> {
Long loanId = loanIdRef.get();
Long capitalizedIncomeAdjustmentTransactionId = loanTransactionHelper
.capitalizedIncomeAdjustment(loanId, capitalizedIncomeTransactionIdRef.get(), "15 April 2024", 15.0).getResourceId();
verifyTRJournalEntries(capitalizedIncomeAdjustmentTransactionId, journalEntry(15, feeIncomeAccount, "DEBIT"), //
journalEntry(15.00, overpaymentAccount, "CREDIT") //
);
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
validateLoanSummaryBalances(loanDetails, 0.0, 151.75, 0.0, 150.00, 15.0);
// Validate Loan goes to Overpaid
assertTrue(loanDetails.getStatus().getOverpaid());
});
}