FINERACT-2382: Repayment schedule for Flat-Cumulative-Multi Disbursement
This commit is contained in:
committed by
Adam Saghy
parent
567b12170b
commit
2269d616dc
+1
-1
@@ -494,7 +494,7 @@ public class DataValidatorBuilder {
|
|||||||
final Integer intValue = Integer.valueOf(this.value.toString());
|
final Integer intValue = Integer.valueOf(this.value.toString());
|
||||||
if (!intValue.equals(number)) {
|
if (!intValue.equals(number)) {
|
||||||
String validationErrorCode = "validation.msg." + this.resource + "." + this.parameter + ".not.equal.to.specified.number";
|
String validationErrorCode = "validation.msg." + this.resource + "." + this.parameter + ".not.equal.to.specified.number";
|
||||||
String defaultEnglishMessage = "The parameter `" + this.parameter + "` must be same as" + number;
|
String defaultEnglishMessage = "The parameter `" + this.parameter + "` must be same as " + number;
|
||||||
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode, defaultEnglishMessage, this.parameter,
|
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode, defaultEnglishMessage, this.parameter,
|
||||||
intValue, number);
|
intValue, number);
|
||||||
this.dataValidationErrors.add(error);
|
this.dataValidationErrors.add(error);
|
||||||
|
|||||||
+2
-2
@@ -1085,7 +1085,7 @@ public final class LoanApplicationTerms {
|
|||||||
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
|
||||||
|
|
||||||
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
|
||||||
.multiply(loanTermFrequencyBigDecimal);
|
.multiply(loanTermFrequencyBigDecimal, mc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BigDecimal calculatePeriodsInLoanTerm() {
|
private BigDecimal calculatePeriodsInLoanTerm() {
|
||||||
@@ -1237,7 +1237,7 @@ public final class LoanApplicationTerms {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.installmentAmountInMultiplesOf != null) {
|
if (this.installmentAmountInMultiplesOf != null) {
|
||||||
Money roundedPrincipalPerPeriod = Money.roundToMultiplesOf(principalPerPeriod, this.installmentAmountInMultiplesOf);
|
Money roundedPrincipalPerPeriod = Money.roundToMultiplesOf(principalPerPeriod, this.installmentAmountInMultiplesOf, mc);
|
||||||
if (interestForThisInstallment != null) {
|
if (interestForThisInstallment != null) {
|
||||||
Money roundedInterestForThisInstallment = Money.roundToMultiplesOf(interestForThisInstallment,
|
Money roundedInterestForThisInstallment = Money.roundToMultiplesOf(interestForThisInstallment,
|
||||||
this.installmentAmountInMultiplesOf);
|
this.installmentAmountInMultiplesOf);
|
||||||
|
|||||||
+2
-27
@@ -214,8 +214,6 @@ public final class LoanApplicationValidator {
|
|||||||
expectedFirstRepaymentOnDate);
|
expectedFirstRepaymentOnDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
validateCumulativeMultiDisburse(loan);
|
|
||||||
|
|
||||||
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
|
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
|
||||||
loan.getLoanProductRelatedDetail().getNumberOfRepayments(), loan.getLoanProductRelatedDetail().getRepayEvery(),
|
loan.getLoanProductRelatedDetail().getNumberOfRepayments(), loan.getLoanProductRelatedDetail().getRepayEvery(),
|
||||||
loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
|
loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
|
||||||
@@ -230,8 +228,6 @@ public final class LoanApplicationValidator {
|
|||||||
expectedFirstRepaymentOnDate);
|
expectedFirstRepaymentOnDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
validateCumulativeMultiDisburse(loan);
|
|
||||||
|
|
||||||
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
|
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
|
||||||
loan.getLoanProductRelatedDetail().getNumberOfRepayments(), loan.getLoanProductRelatedDetail().getRepayEvery(),
|
loan.getLoanProductRelatedDetail().getNumberOfRepayments(), loan.getLoanProductRelatedDetail().getRepayEvery(),
|
||||||
loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
|
loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
|
||||||
@@ -1742,16 +1738,8 @@ public final class LoanApplicationValidator {
|
|||||||
if (transactionProcessingStrategyCode != null) {
|
if (transactionProcessingStrategyCode != null) {
|
||||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed(LoanApiConstants.interestTypeParameterName,
|
final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed(LoanApiConstants.interestTypeParameterName,
|
||||||
element, Locale.getDefault());
|
element, Locale.getDefault());
|
||||||
String processorCode = loanRepaymentScheduleTransactionProcessorFactory
|
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType).ignoreIfNull()
|
||||||
.determineProcessor(transactionProcessingStrategyCode).getCode();
|
.inMinMaxRange(0, 1);
|
||||||
boolean isProgressive = "advanced-payment-allocation-strategy".equals(processorCode);
|
|
||||||
if (isProgressive) {
|
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType)
|
|
||||||
.ignoreIfNull().inMinMaxRange(0, 1);
|
|
||||||
} else {
|
|
||||||
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType)
|
|
||||||
.ignoreIfNull().integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (loan.isCumulativeSchedule()) {
|
if (loan.isCumulativeSchedule()) {
|
||||||
@@ -2208,19 +2196,6 @@ public final class LoanApplicationValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateCumulativeMultiDisburse(Loan loan) {
|
|
||||||
if (loan.isCumulativeSchedule() && loan.isMultiDisburmentLoan()
|
|
||||||
&& loan.getLoanProductRelatedDetail().getInterestMethod().isFlat()) {
|
|
||||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
|
||||||
final ApiParameterError error = ApiParameterError.generalError(
|
|
||||||
"validation.msg.loan.cumulative.multidisburse.does.not.support.flat.interest.mode",
|
|
||||||
"Cumulative multidisburse loan does NOT support FLAT interest mode.");
|
|
||||||
dataValidationErrors.add(error);
|
|
||||||
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
|
|
||||||
dataValidationErrors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Calendar getCalendarInstance(Loan loan) {
|
private Calendar getCalendarInstance(Loan loan) {
|
||||||
CalendarInstance calendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
|
CalendarInstance calendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
|
||||||
CalendarEntityType.LOANS.getValue());
|
CalendarEntityType.LOANS.getValue());
|
||||||
|
|||||||
+1
-7
@@ -1059,13 +1059,7 @@ public final class LoanProductDataValidator {
|
|||||||
|
|
||||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed(INTEREST_TYPE, element, Locale.getDefault());
|
final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed(INTEREST_TYPE, element, Locale.getDefault());
|
||||||
|
|
||||||
boolean isProgressive = isProgressive(element, loanProduct);
|
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull().inMinMaxRange(0, 1);
|
||||||
if (isProgressive) {
|
|
||||||
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull().inMinMaxRange(0, 1);
|
|
||||||
} else {
|
|
||||||
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull()
|
|
||||||
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final String overAppliedCalculationType = this.fromApiJsonHelper.extractStringNamed(OVER_APPLIED_CALCULATION_TYPE, element);
|
final String overAppliedCalculationType = this.fromApiJsonHelper.extractStringNamed(OVER_APPLIED_CALCULATION_TYPE, element);
|
||||||
|
|||||||
+69
@@ -6251,6 +6251,75 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UC158: Repayment schedule handling for flat cumulative multi-disbursement
|
||||||
|
@Test
|
||||||
|
public void uc158() {
|
||||||
|
AtomicLong loanIdRef = new AtomicLong();
|
||||||
|
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
|
||||||
|
final BigDecimal principalAmount = BigDecimal.valueOf(2000.0);
|
||||||
|
|
||||||
|
runAt("1 January 2024", () -> {
|
||||||
|
// Create a Cumulative Multidisbursal and Flat Interest Type
|
||||||
|
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(
|
||||||
|
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct().interestType(InterestType.FLAT).daysInMonthType(30)//
|
||||||
|
.transactionProcessingStrategyCode(LoanProductTestBuilder.DEFAULT_STRATEGY).interestRateFrequencyType(YEARS)
|
||||||
|
.daysInYearType(365).loanScheduleType(LoanScheduleType.CUMULATIVE.toString()).repaymentEvery(1)
|
||||||
|
.installmentAmountInMultiplesOf(null)//
|
||||||
|
.repaymentFrequencyType(2L)//
|
||||||
|
);
|
||||||
|
assertNotNull(loanProductResponse.getResourceId());
|
||||||
|
|
||||||
|
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "1 January 2024",
|
||||||
|
principalAmount.doubleValue(), 3).interestCalculationPeriodType(1).interestType(InterestType.FLAT)//
|
||||||
|
.transactionProcessingStrategyCode(LoanProductTestBuilder.DEFAULT_STRATEGY).interestRateFrequencyType(YEARS)//
|
||||||
|
.interestRatePerPeriod(BigDecimal.valueOf(7.0))//
|
||||||
|
.repaymentEvery(1)//
|
||||||
|
.repaymentFrequencyType(MONTHS)//
|
||||||
|
.loanTermFrequency(3)//
|
||||||
|
.loanTermFrequencyType(MONTHS);
|
||||||
|
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
|
||||||
|
|
||||||
|
loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().approvedLoanAmount(principalAmount)
|
||||||
|
.dateFormat(DATETIME_PATTERN).approvedOnDate("1 January 2024").locale("en"));
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("1 January 2024").dateFormat(DATETIME_PATTERN).locale("en")
|
||||||
|
.transactionAmount(BigDecimal.valueOf(1000.00)));
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 1017.50, 0.00, 1000.00, 0.00, null);
|
||||||
|
validatePeriod(loanDetails, 0, LocalDate.of(2024, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
|
||||||
|
null, null, null, null);
|
||||||
|
validatePeriod(loanDetails, 1, LocalDate.of(2024, 2, 1), null, 666.67, 333.33, 0.00, 333.33, 0.0, 0.0, 0.0, 0.00, 0.00, 0.00,
|
||||||
|
5.83, 0.00, 5.83, 0.00, 0.00);
|
||||||
|
validatePeriod(loanDetails, 2, LocalDate.of(2024, 3, 1), null, 333.34, 333.33, 0.00, 333.33, 0.0, 0.0, 0.0, 0.00, 0.00, 0.00,
|
||||||
|
5.83, 0.00, 5.83, 0.00, 0.00);
|
||||||
|
validatePeriod(loanDetails, 3, LocalDate.of(2024, 4, 1), null, 0.00, 333.34, 0.00, 333.34, 0.0, 0.0, 0.00, 0.00, 0.00, 0.00,
|
||||||
|
5.84, 0.00, 5.84, 0.00, 0.00);
|
||||||
|
loanIdRef.set(loanResponse.getLoanId());
|
||||||
|
});
|
||||||
|
|
||||||
|
runAt("15 January 2024", () -> {
|
||||||
|
final Long loanId = loanIdRef.get();
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanId, new PostLoansLoanIdRequest().actualDisbursementDate("15 January 2024")
|
||||||
|
.dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(BigDecimal.valueOf(500.0)));
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
|
||||||
|
validateLoanSummaryBalances(loanDetails, 1526.25, 0.00, 1500.00, 0.00, null);
|
||||||
|
validatePeriod(loanDetails, 0, LocalDate.of(2024, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
|
||||||
|
null, null, null, null);
|
||||||
|
validatePeriod(loanDetails, 1, LocalDate.of(2024, 1, 15), null, 500.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
|
||||||
|
null, null, null, null);
|
||||||
|
validatePeriod(loanDetails, 2, LocalDate.of(2024, 2, 1), null, 1000.00, 500.00, 0.00, 500.00, 0.0, 0.0, 0.0, 0.00, 0.00, 0.00,
|
||||||
|
8.75, 0.00, 8.75, 0.00, 0.00);
|
||||||
|
validatePeriod(loanDetails, 3, LocalDate.of(2024, 3, 1), null, 500.00, 500.00, 0.00, 500.00, 0.0, 0.0, 0.0, 0.00, 0.00, 0.00,
|
||||||
|
8.75, 0.00, 8.75, 0.00, 0.00);
|
||||||
|
validatePeriod(loanDetails, 4, LocalDate.of(2024, 4, 1), null, 0.00, 500.00, 0.00, 500.00, 0.0, 0.0, 0.00, 0.00, 0.00, 0.00,
|
||||||
|
8.75, 0.00, 8.75, 0.00, 0.00);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
|
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
|
||||||
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
|
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
|
||||||
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
|
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");
|
||||||
|
|||||||
Reference in New Issue
Block a user