FINERACT-2081: Loan account data additional fields for summary and delinquency
This commit is contained in:
committed by
Adam Saghy
parent
4ac102f4e0
commit
79f32d32a3
+80
@@ -70,6 +70,7 @@ import org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper;
|
||||
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.PeriodicAccrualAccountingHelper;
|
||||
import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
|
||||
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
|
||||
@@ -4601,6 +4602,85 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
});
|
||||
}
|
||||
|
||||
// UC144: Advanced payment allocation with Interest,
|
||||
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
|
||||
// 1. Create a Loan product with Adv. Pment. Alloc. and with 5% Interest, 360/30, 1 repayment per month
|
||||
// 2. Submit Loan and approve
|
||||
// 3. Disburse
|
||||
// 4. Validate Repayment Schedule
|
||||
@Test
|
||||
public void uc144() {
|
||||
final String operationDate = "1 January 2024";
|
||||
AtomicLong createdLoanId = new AtomicLong();
|
||||
runAt(operationDate, () -> {
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(12.3).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).interestRateFrequencyType(YEARS)
|
||||
.daysInMonthType(DaysInMonthType.ACTUAL.getValue()).daysInYearType(DaysInYearType.DAYS_365.getValue())
|
||||
.numberOfRepayments(4)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(2L)//
|
||||
.allowPartialPeriodInterestCalcualtion(false)//
|
||||
.multiDisburseLoan(false)//
|
||||
.disallowExpectedDisbursements(null)//
|
||||
.allowApprovedDisbursedAmountsOverApplied(null)//
|
||||
.overAppliedCalculationType(null)//
|
||||
.overAppliedNumber(null)//
|
||||
.installmentAmountInMultiplesOf(null)//
|
||||
;//
|
||||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
||||
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, 100.0, 5);
|
||||
|
||||
applicationRequest = applicationRequest.numberOfRepayments(5).loanTermFrequency(5).loanTermFrequencyType(2)
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(12.3)).interestCalculationPeriodType(DAYS)
|
||||
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY).repaymentEvery(1)
|
||||
.repaymentFrequencyType(2);
|
||||
|
||||
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
||||
createdLoanId.set(loanResponse.getLoanId());
|
||||
|
||||
loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest()
|
||||
.approvedLoanAmount(BigDecimal.valueOf(100)).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"));
|
||||
|
||||
// After Disbursement we are expecting no Accrual transactions
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||
assertEquals(0.0, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
|
||||
assertEquals(0.0, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
|
||||
});
|
||||
|
||||
// Not Due yet
|
||||
runAt("30 January 2024", () -> {
|
||||
// Generate the Accruals
|
||||
final PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec,
|
||||
responseSpec);
|
||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("30 January 2024");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||
assertEquals(0.0, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
|
||||
assertEquals(0.97, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
|
||||
|
||||
// Partial interest repayment
|
||||
addRepaymentForLoan(createdLoanId.get(), 20.50, "30 January 2024");
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||
assertEquals(0.0, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
|
||||
assertEquals(0.05, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
|
||||
});
|
||||
|
||||
// Not Due and Due Interest
|
||||
runAt("20 February 2024", () -> {
|
||||
final PeriodicAccrualAccountingHelper periodicAccrualAccountingHelper = new PeriodicAccrualAccountingHelper(requestSpec,
|
||||
responseSpec);
|
||||
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting("20 February 2024");
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||
assertEquals(0.12, loanDetails.getSummary().getTotalUnpaidAccruedDueInterest());
|
||||
assertEquals(0.52, loanDetails.getSummary().getTotalUnpaidAccruedNotDueInterest());
|
||||
});
|
||||
}
|
||||
|
||||
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
|
||||
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
|
||||
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
|
||||
|
||||
+114
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.DAYS;
|
||||
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
|
||||
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -55,7 +56,11 @@ import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||
import org.apache.fineract.client.models.PostDelinquencyBucketResponse;
|
||||
import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
|
||||
import org.apache.fineract.client.models.PostLoansDelinquencyActionResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
|
||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||
import org.apache.fineract.client.models.PostLoansRequest;
|
||||
import org.apache.fineract.client.models.PostLoansResponse;
|
||||
import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
|
||||
import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
|
||||
import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
|
||||
@@ -1444,6 +1449,115 @@ public class DelinquencyBucketsIntegrationTest extends BaseLoanIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanDelinquencyDataWithAmountPerPortions() {
|
||||
// Given
|
||||
final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
|
||||
ArrayList<Integer> rangeIds = new ArrayList<>();
|
||||
// First Range
|
||||
String jsonRange = DelinquencyRangesHelper.getAsJSON(4, 30);
|
||||
PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec,
|
||||
jsonRange);
|
||||
rangeIds.add(delinquencyRangeResponse.getResourceId());
|
||||
GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
|
||||
delinquencyRangeResponse.getResourceId());
|
||||
|
||||
String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
|
||||
PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
|
||||
responseSpec, jsonBucket);
|
||||
assertNotNull(delinquencyBucketResponse);
|
||||
final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
|
||||
delinquencyBucketResponse.getResourceId());
|
||||
|
||||
// Client and Loan account creation
|
||||
final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
|
||||
final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
|
||||
Math.toIntExact(delinquencyBucket.getId()), null);
|
||||
assertNotNull(getLoanProductsProductResponse);
|
||||
log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
|
||||
assertEquals(getLoanProductsProductResponse.getDelinquencyBucket().getName(), delinquencyBucket.getName());
|
||||
|
||||
final LocalDate todaysDate = Utils.getLocalDateOfTenant();
|
||||
log.info("Local date of Tenant: {}", todaysDate);
|
||||
|
||||
// Older date to have more than one overdue installment
|
||||
final LocalDate transactionDate = todaysDate.minusDays(50);
|
||||
final String operationDate = Utils.dateFormatter.format(transactionDate);
|
||||
|
||||
final Double amount = 2000.0;
|
||||
PostLoansRequest applicationRequest = applyLoanRequest(clientId, getLoanProductsProductResponse.getId(), operationDate, amount, 4);
|
||||
|
||||
applicationRequest = applicationRequest.numberOfRepayments(5).loanTermFrequency(5).loanTermFrequencyType(2)
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(12.3)).interestCalculationPeriodType(DAYS).repaymentEvery(1)
|
||||
.repaymentFrequencyType(2);
|
||||
|
||||
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
||||
final Long loanId = loanResponse.getResourceId();
|
||||
|
||||
loanTransactionHelper.approveLoan(loanId, new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(amount))
|
||||
.dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanId, new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
|
||||
.dateFormat(DATETIME_PATTERN).transactionAmount(BigDecimal.valueOf(amount)).locale("en"));
|
||||
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
log.info("Loan Delinquency Range after Disbursement {}", loanDetails.getDelinquencyRange().getClassification());
|
||||
assertNotNull(loanDetails.getDelinquent());
|
||||
log.info("Loan Delinquency Data {} {}", loanDetails.getDelinquent().getDelinquentPrincipal(),
|
||||
loanDetails.getDelinquent().getDelinquentInterest());
|
||||
assertNotNull(loanDetails.getDelinquent().getDelinquentPrincipal());
|
||||
assertEquals(305.91, loanDetails.getDelinquent().getDelinquentPrincipal());
|
||||
assertNotNull(loanDetails.getDelinquent().getDelinquentInterest());
|
||||
assertEquals(250.72, loanDetails.getDelinquent().getDelinquentInterest());
|
||||
|
||||
// Apply a partial repayment to move only the interest
|
||||
PostLoansLoanIdTransactionsResponse loansLoanIdTransactions = loanTransactionHelper.makeLoanRepayment(operationDate, 120f,
|
||||
loanId.intValue());
|
||||
assertNotNull(loansLoanIdTransactions);
|
||||
log.info("Loan repayment transaction id {}", loansLoanIdTransactions.getResourceId());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
assertNotNull(loanDetails.getDelinquent());
|
||||
assertNotNull(loanDetails.getDelinquencyRange().getClassification());
|
||||
assertEquals(305.91, loanDetails.getDelinquent().getDelinquentPrincipal());
|
||||
assertEquals(130.72, loanDetails.getDelinquent().getDelinquentInterest());
|
||||
|
||||
// Apply a repayment to cover interest and part of the principal
|
||||
loansLoanIdTransactions = loanTransactionHelper.makeLoanRepayment(operationDate, 330.72f, loanId.intValue());
|
||||
assertNotNull(loansLoanIdTransactions);
|
||||
log.info("Loan repayment transaction id {}", loansLoanIdTransactions.getResourceId());
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
assertNotNull(loanDetails.getDelinquent());
|
||||
assertNotNull(loanDetails.getDelinquencyRange().getClassification());
|
||||
assertEquals(105.91, loanDetails.getDelinquent().getDelinquentPrincipal());
|
||||
assertEquals(0.0, loanDetails.getDelinquent().getDelinquentInterest());
|
||||
|
||||
// Apply a repayment to cover the remain principal
|
||||
loansLoanIdTransactions = loanTransactionHelper.makeLoanRepayment(operationDate, 105.91f, loanId.intValue());
|
||||
assertNotNull(loansLoanIdTransactions);
|
||||
log.info("Loan repayment transaction id {}", loansLoanIdTransactions.getResourceId());
|
||||
// Loan without Delinquency Classification
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
assertNotNull(loanDetails.getDelinquent());
|
||||
assertNull(loanDetails.getDelinquencyRange());
|
||||
assertEquals(0.0, loanDetails.getDelinquent().getDelinquentPrincipal());
|
||||
assertEquals(0.0, loanDetails.getDelinquent().getDelinquentInterest());
|
||||
|
||||
// Undo the last repayment transaction we must to have pending the principal
|
||||
PostLoansLoanIdTransactionsResponse reverseRepayment = loanTransactionHelper.reverseLoanTransaction(loanId,
|
||||
loansLoanIdTransactions.getResourceId(), new PostLoansLoanIdTransactionsTransactionIdRequest().dateFormat("dd MMMM yyyy")
|
||||
.transactionDate(operationDate).transactionAmount(0.0).locale("en"));
|
||||
assertNotNull(reverseRepayment);
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||
assertNotNull(loanDetails.getDelinquent());
|
||||
assertNotNull(loanDetails.getDelinquencyRange().getClassification());
|
||||
assertEquals(105.91, loanDetails.getDelinquent().getDelinquentPrincipal());
|
||||
assertEquals(0.0, loanDetails.getDelinquent().getDelinquentInterest());
|
||||
}
|
||||
|
||||
private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
|
||||
final Integer delinquencyBucketId, final String inArrearsTolerance) {
|
||||
final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().withInArrearsTolerance(inArrearsTolerance).build(null,
|
||||
|
||||
Reference in New Issue
Block a user