FINERACT-2308: Allow approval and disbursal above loan applied amount with non multibursal loan product
This commit is contained in:
committed by
Adam Saghy
parent
38a5686df8
commit
bb29932658
+1
-1
@@ -410,7 +410,7 @@ public class LoanProduct extends AbstractPersistableCustom<Long> {
|
||||
}
|
||||
|
||||
if (this.allowApprovedDisbursedAmountsOverApplied) {
|
||||
if (!this.disallowExpectedDisbursements) {
|
||||
if (this.isMultiDisburseLoan() && !this.disallowExpectedDisbursements) {
|
||||
throw new LoanProductGeneralRuleException(
|
||||
"disallowExpectedDisbursements.not.set.allowApprovedDisbursedAmountsOverApplied.cant.be.set",
|
||||
"Disallow Expected Disbursals Not Set - Allow Approved / Disbursed Amounts Over Applied Can't Be Set");
|
||||
|
||||
+1
-1
@@ -2124,7 +2124,7 @@ public final class LoanApplicationValidator {
|
||||
}
|
||||
|
||||
private void compareApprovedToProposedPrincipal(Loan loan, BigDecimal approvedLoanAmount) {
|
||||
if (loan.loanProduct().isDisallowExpectedDisbursements() && loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
|
||||
if (loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
|
||||
BigDecimal maxApprovedLoanAmount = getOverAppliedMax(loan);
|
||||
if (approvedLoanAmount.compareTo(maxApprovedLoanAmount) > 0) {
|
||||
final String errorMessage = "Loan approved amount can't be greater than maximum applied loan amount calculation.";
|
||||
|
||||
+23
-15
@@ -37,26 +37,34 @@ public final class LoanDisbursementValidator {
|
||||
final BigDecimal totalDisbursed) {
|
||||
final BigDecimal capitalizedIncome = loan.getSummary().getTotalCapitalizedIncome();
|
||||
if (loan.loanProduct().isDisallowExpectedDisbursements() && loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
|
||||
final BigDecimal maxDisbursedAmount = loanApplicationValidator.getOverAppliedMax(loan);
|
||||
if (totalDisbursed.add(capitalizedIncome).compareTo(maxDisbursedAmount) > 0) {
|
||||
final String errorMessage = String.format(
|
||||
"Loan disbursal amount can't be greater than maximum applied loan amount calculation. "
|
||||
+ "Total disbursed amount: %s Maximum disbursal amount: %s",
|
||||
totalDisbursed.stripTrailingZeros().toPlainString(), maxDisbursedAmount.stripTrailingZeros().toPlainString());
|
||||
throw new InvalidLoanStateTransitionException("disbursal",
|
||||
"amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", errorMessage, disbursedAmount,
|
||||
maxDisbursedAmount);
|
||||
}
|
||||
validateOverMaximumAmount(loan, totalDisbursed, capitalizedIncome);
|
||||
} else {
|
||||
if ((totalDisbursed.compareTo(loan.getApprovedPrincipal()) > 0)
|
||||
|| (totalDisbursed.add(capitalizedIncome).compareTo(loan.getApprovedPrincipal()) > 0)) {
|
||||
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved principal ";
|
||||
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.principal", totalDisbursed,
|
||||
loan.getApprovedPrincipal());
|
||||
if (loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
|
||||
validateOverMaximumAmount(loan, disbursedAmount, capitalizedIncome);
|
||||
} else {
|
||||
if ((totalDisbursed.compareTo(loan.getApprovedPrincipal()) > 0)
|
||||
|| (totalDisbursed.add(capitalizedIncome).compareTo(loan.getApprovedPrincipal()) > 0)) {
|
||||
final String errorMsg = "Loan can't be disbursed,disburse amount is exceeding approved principal ";
|
||||
throw new LoanDisbursalException(errorMsg, "disburse.amount.must.be.less.than.approved.principal", totalDisbursed,
|
||||
loan.getApprovedPrincipal());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void validateOverMaximumAmount(final Loan loan, final BigDecimal totalDisbursed, final BigDecimal capitalizedIncome) {
|
||||
final BigDecimal maxDisbursedAmount = loanApplicationValidator.getOverAppliedMax(loan);
|
||||
if (totalDisbursed.add(capitalizedIncome).compareTo(maxDisbursedAmount) > 0) {
|
||||
final String errorMessage = String.format(
|
||||
"Loan disbursal amount can't be greater than maximum applied loan amount calculation. "
|
||||
+ "Total disbursed amount: %s Maximum disbursal amount: %s",
|
||||
totalDisbursed.stripTrailingZeros().toPlainString(), maxDisbursedAmount.stripTrailingZeros().toPlainString());
|
||||
throw new InvalidLoanStateTransitionException("disbursal",
|
||||
"amount.can't.be.greater.than.maximum.applied.loan.amount.calculation", errorMessage, totalDisbursed,
|
||||
maxDisbursedAmount);
|
||||
}
|
||||
}
|
||||
|
||||
public void validateDisburseAmountNotExceedingApprovedAmount(final Loan loan, final BigDecimal diff,
|
||||
final BigDecimal principalDisbursed) {
|
||||
if (!loan.loanProduct().isMultiDisburseLoan() && diff.compareTo(BigDecimal.ZERO) < 0) {
|
||||
|
||||
+2
-3
@@ -146,10 +146,10 @@ public class LoanDisbursementService {
|
||||
disbursementDetails.updatePrincipal(principalDisbursed);
|
||||
}
|
||||
}
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
if (loan.loanProduct().isMultiDisburseLoan()) {
|
||||
Collection<LoanDisbursementDetails> loanDisburseDetails = loan.getDisbursementDetails();
|
||||
BigDecimal setPrincipalAmount = BigDecimal.ZERO;
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
for (LoanDisbursementDetails disbursementDetails : loanDisburseDetails) {
|
||||
if (disbursementDetails.actualDisbursementDate() != null) {
|
||||
setPrincipalAmount = setPrincipalAmount.add(disbursementDetails.principal());
|
||||
@@ -157,12 +157,11 @@ public class LoanDisbursementService {
|
||||
totalAmount = totalAmount.add(disbursementDetails.principal());
|
||||
}
|
||||
loan.getLoanRepaymentScheduleDetail().setPrincipal(setPrincipalAmount);
|
||||
loanDisbursementValidator.compareDisbursedToApprovedOrProposedPrincipal(loan, disburseAmount.getAmount(), totalAmount);
|
||||
} else {
|
||||
loan.getLoanRepaymentScheduleDetail()
|
||||
.setPrincipal(loan.getLoanRepaymentScheduleDetail().getPrincipal().minus(diff).getAmount());
|
||||
}
|
||||
loanDisbursementValidator.validateDisburseAmountNotExceedingApprovedAmount(loan, diff, principalDisbursed);
|
||||
loanDisbursementValidator.compareDisbursedToApprovedOrProposedPrincipal(loan, disburseAmount.getAmount(), totalAmount);
|
||||
}
|
||||
return disburseAmount;
|
||||
}
|
||||
|
||||
+55
@@ -5946,6 +5946,61 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
||||
});
|
||||
}
|
||||
|
||||
// UC155: Validate allowApprovedDisbursedAmountsOverApplied setting on Non MultiDisbursement Loan Product
|
||||
// 1. Create a Loan product with allowApprovedDisbursedAmountsOverApplied in true using overAppliedCalculationType
|
||||
// flat amount
|
||||
// 2. Submit Loan with 1,000
|
||||
// 3. Approve the Loan with 1,300
|
||||
// 4. Disburse the Loan with 1,450
|
||||
@Test
|
||||
public void uc155() {
|
||||
final String operationDate = "1 January 2024";
|
||||
AtomicLong createdLoanId = new AtomicLong();
|
||||
runAt(operationDate, () -> {
|
||||
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||
PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation()
|
||||
.interestRatePerPeriod(12.0).interestCalculationPeriodType(RepaymentFrequencyType.DAYS).numberOfRepayments(4)//
|
||||
.repaymentEvery(1)//
|
||||
.repaymentFrequencyType(1L)//
|
||||
.allowPartialPeriodInterestCalcualtion(false)//
|
||||
.installmentAmountInMultiplesOf(null)//
|
||||
.multiDisburseLoan(false)//
|
||||
.disallowExpectedDisbursements(null)//
|
||||
.allowApprovedDisbursedAmountsOverApplied(true)//
|
||||
.overAppliedCalculationType("flat")//
|
||||
.overAppliedNumber(500)//
|
||||
;//
|
||||
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
|
||||
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, 1000.0, 4)
|
||||
.transactionProcessingStrategyCode("advanced-payment-allocation-strategy")//
|
||||
.interestRatePerPeriod(BigDecimal.valueOf(12.0));
|
||||
|
||||
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
||||
createdLoanId.set(loanResponse.getLoanId());
|
||||
|
||||
loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest()
|
||||
.approvedLoanAmount(BigDecimal.valueOf(1300)).dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en"));
|
||||
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||
assertTrue(loanDetails.getStatus().getWaitingForDisbursal());
|
||||
assertEquals("1300.000000", loanDetails.getApprovedPrincipal().toString());
|
||||
|
||||
CallFailedRuntimeException callFailedRuntimeException = Assertions.assertThrows(CallFailedRuntimeException.class,
|
||||
() -> loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
|
||||
new PostLoansLoanIdRequest().actualDisbursementDate(operationDate).dateFormat(DATETIME_PATTERN).locale("en")
|
||||
.transactionAmount(BigDecimal.valueOf(1550.0))));
|
||||
|
||||
Assertions.assertTrue(callFailedRuntimeException.getMessage()
|
||||
.contains("Loan disbursal amount can't be greater than maximum applied loan amount calculation"));
|
||||
|
||||
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate)
|
||||
.dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(BigDecimal.valueOf(1450.0)));
|
||||
|
||||
loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get());
|
||||
assertTrue(loanDetails.getStatus().getActive());
|
||||
assertEquals("1450.000000", loanDetails.getSummary().getPrincipalOutstanding().toString());
|
||||
});
|
||||
}
|
||||
|
||||
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
|
||||
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
|
||||
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
|
||||
|
||||
Reference in New Issue
Block a user