FINERACT-1981: Prepayment calculation

This commit is contained in:
Adam Saghy
2024-11-06 15:04:17 +01:00
parent 474623b6e3
commit 492ec44cef
16 changed files with 391 additions and 255 deletions
@@ -5518,6 +5518,197 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
});
}
// UC151: Validate Prepayment calculation till Pay-off date
// 1. Create a Loan product with Adv. Pment. Alloc. and with Declining Balance, 7% interest rate
// 2. Submit Loan, approve and Disburse
// 3. Validate Pay-off amounts on 31/12/2023, 01/01/2024, 02/01/2024, 01/02/2024, 02/02/2024, 15/02/2024, 01/03/2024
// 4. Pay 1st installment fully on the due date of the 1st period
// 5. Validate Pay-off amounts on 31/12/2023, 01/01/2024, 02/01/2024, 01/02/2024, 02/02/2024, 15/02/2024, 01/03/2024
// 6. Pay 2nd installment partially in the middle of 2nd period (15/02/2024)
// 7. Validate Pay-off amounts on 31/12/2023, 01/01/2024, 02/01/2024, 01/02/2024, 02/02/2024, 15/02/2024, 01/03/2024
// 8. Prepay the loan on 15/02/2024
@Test
public void uc151() {
String operationDate = "01 January 2024";
AtomicLong createdLoanId = new AtomicLong();
runAt(operationDate, () -> {
Long clientId = client.getClientId();
PostLoanProductsRequest product = create4IProgressive().interestRateFrequencyType(YEARS).numberOfRepayments(6)//
.repaymentEvery(1)//
.repaymentFrequencyType(1L)//
.allowPartialPeriodInterestCalcualtion(false)//
.multiDisburseLoan(true)//
.maxTrancheCount(10)//
.disallowExpectedDisbursements(true)//
.allowApprovedDisbursedAmountsOverApplied(null)//
.overAppliedCalculationType(null)//
.overAppliedNumber(null)//
.installmentAmountInMultiplesOf(null)//
.loanScheduleType(LoanScheduleType.PROGRESSIVE.toString()) //
;//
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
PostLoansRequest applicationRequest = applyLP2ProgressiveLoanRequest(clientId, loanProductResponse.getResourceId(),
operationDate, 1000.0, 7.0, 6, null);
applicationRequest = applicationRequest.interestCalculationPeriodType(DAYS)
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY);
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
createdLoanId.set(loanResponse.getLoanId());
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000.0)).dateFormat(DATETIME_PATTERN)
.approvedOnDate(operationDate).locale("en"));
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
// Before 1st disbursement date
HashMap prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2023, 12, 31));
assertEquals(100.0f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
// On 1st day
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 1, 1));
assertEquals(100.0f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
// On 2nd day
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 1, 2));
assertEquals(100.02f, prepayAmounts.get("amount"));
assertEquals(0.02f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
// On due date of 1st period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 1));
assertEquals(100.58f, prepayAmounts.get("amount"));
assertEquals(0.58f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
// On the 1st day of 2nd period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 2));
assertEquals(100.60f, prepayAmounts.get("amount"));
assertEquals(0.60f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
// In the middle of 2nd period (15 Feb)
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 15));
assertEquals(100.86f, prepayAmounts.get("amount"));
assertEquals(0.86f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
// On the due date of 2nd period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 3, 1));
assertEquals(101.16f, prepayAmounts.get("amount"));
assertEquals(1.16f, prepayAmounts.get("interestPortion"));
assertEquals(100.0f, prepayAmounts.get("principalPortion"));
});
String repaymentDate = "01 February 2024";
runAt(repaymentDate, () -> {
loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest()
.transactionDate(repaymentDate).dateFormat("dd MMMM yyyy").locale("en").transactionAmount(17.01));
// Before 1st disbursement date
HashMap prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2023, 12, 31));
assertEquals(83.57f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
// On 1st day
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 1, 1));
assertEquals(83.57f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
// On 2nd day
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 1, 2));
assertEquals(83.57f, prepayAmounts.get("amount"));
assertEquals(0.00f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
// On due date of 1st period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 1));
assertEquals(83.57f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
// On the 1st day of 2nd period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 2));
assertEquals(83.59f, prepayAmounts.get("amount"));
assertEquals(0.02f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
// In the middle of 2nd period (15 Feb)
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 15));
assertEquals(83.81f, prepayAmounts.get("amount"));
assertEquals(0.24f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
// On the due date of 2nd period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 3, 1));
assertEquals(84.06f, prepayAmounts.get("amount"));
assertEquals(0.49f, prepayAmounts.get("interestPortion"));
assertEquals(83.57f, prepayAmounts.get("principalPortion"));
});
String secondRepaymentDate = "15 February 2024";
runAt(secondRepaymentDate, () -> {
loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest()
.transactionDate(secondRepaymentDate).dateFormat("dd MMMM yyyy").locale("en").transactionAmount(5.0));
// Before 1st disbursement date
HashMap prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2023, 12, 31));
assertEquals(78.57f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
// On 1st day
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 1, 1));
assertEquals(78.57f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
// On 2nd day
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 1, 2));
assertEquals(78.57f, prepayAmounts.get("amount"));
assertEquals(0.00f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
// On due date of 1st period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 1));
assertEquals(78.57f, prepayAmounts.get("amount"));
assertEquals(0.0f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
// On the 1st day of 2nd period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 2));
assertEquals(78.59f, prepayAmounts.get("amount"));
assertEquals(0.02f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
// In the middle of 2nd period (15 Feb)
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 2, 15));
assertEquals(78.81f, prepayAmounts.get("amount"));
assertEquals(0.24f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
// On the due date of 2nd period
prepayAmounts = loanTransactionHelper.getPrepayAmount(requestSpec, responseSpec, createdLoanId.intValue(),
LocalDate.of(2024, 3, 1));
assertEquals(79.05f, prepayAmounts.get("amount"));
assertEquals(0.48f, prepayAmounts.get("interestPortion"));
assertEquals(78.57f, prepayAmounts.get("principalPortion"));
loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest()
.transactionDate(secondRepaymentDate).dateFormat("dd MMMM yyyy").locale("en").transactionAmount(78.81));
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
assertTrue(loanDetails.getStatus().getClosedObligationsMet());
});
}
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
@@ -867,8 +867,8 @@ public abstract class BaseLoanIntegrationTest {
return postLoansRequest;
}
protected PostLoansRequest applyPin4ProgressiveLoanRequest(Long clientId, Long loanProductId, String loanDisbursementDate,
Double amount, Double interestRate, int numberOfRepayments, Consumer<PostLoansRequest> customizer) {
protected PostLoansRequest applyLP2ProgressiveLoanRequest(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount,
Double interestRate, int numberOfRepayments, Consumer<PostLoansRequest> customizer) {
PostLoansRequest postLoansRequest = new PostLoansRequest().clientId(clientId)
.transactionProcessingStrategyCode(ADVANCED_PAYMENT_ALLOCATION_STRATEGY).productId(loanProductId)
@@ -912,7 +912,7 @@ public abstract class BaseLoanIntegrationTest {
protected Long applyAndApproveProgressiveLoan(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount,
Double interestRate, int numberOfRepayments, Consumer<PostLoansRequest> customizer) {
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyPin4ProgressiveLoanRequest(clientId, loanProductId,
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applyLP2ProgressiveLoanRequest(clientId, loanProductId,
loanDisbursementDate, amount, interestRate, numberOfRepayments, customizer));
PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
@@ -44,7 +44,7 @@ public class LoanPayOffTest extends BaseLoanIntegrationTest {
clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive());
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(
applyPin4ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
applyLP2ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
loanId = postLoansResponse.getLoanId();
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "01 June 2024"));
disburseLoan(loanId, BigDecimal.valueOf(250.0), "01 June 2024");
@@ -318,7 +318,7 @@ public class LoanRefundTransactionTest extends BaseLoanIntegrationTest {
final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(
create4IProgressive().supportedInterestRefundTypes(new ArrayList<>()).addSupportedInterestRefundTypesItem(refundType));
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(
applyPin4ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
applyLP2ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
Long loanId = postLoansResponse.getLoanId();
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "01 June 2024"));
disburseLoan(loanId, BigDecimal.valueOf(1000.0), "01 June 2024");
@@ -44,7 +44,7 @@ public class LoanTransactionBackdatedProgressiveTest extends BaseLoanIntegration
clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
final PostLoanProductsResponse loanProductsResponse = loanProductHelper.createLoanProduct(create4IProgressive());
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(
applyPin4ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
applyLP2ProgressiveLoanRequest(clientId, loanProductsResponse.getResourceId(), "01 June 2024", 1000.0, 10.0, 4, null));
loanId = postLoansResponse.getLoanId();
loanTransactionHelper.approveLoan(loanId, approveLoanRequest(1000.0, "01 June 2024"));
disburseLoan(loanId, BigDecimal.valueOf(250.0), "01 June 2024");
@@ -1488,6 +1488,15 @@ public class LoanTransactionHelper extends IntegrationTest {
return response;
}
public HashMap getPrepayAmount(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID,
final LocalDate transactionDate) {
final String URL = "/fineract-provider/api/v1/loans/" + loanID
+ "/transactions/template?command=prepayLoan&locale=en&dateFormat=yyyy-MM-dd&transactionDate=" + transactionDate + "&"
+ Utils.TENANT_IDENTIFIER;
final HashMap response = Utils.performServerGet(requestSpec, responseSpec, URL, "");
return response;
}
private String createLoanRefundTransferURL() {
return "/fineract-provider/api/v1/accounttransfers/refundByTransfer?tenantIdentifier=default";
}