Skip to content

Commit 2269d61

Browse files
Jose Alberto Hernandezadamsaghy
authored andcommitted
FINERACT-2382: Repayment schedule for Flat-Cumulative-Multi Disbursement
1 parent 567b121 commit 2269d61

File tree

5 files changed

+75
-37
lines changed

5 files changed

+75
-37
lines changed

fineract-core/src/main/java/org/apache/fineract/infrastructure/core/data/DataValidatorBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ public DataValidatorBuilder integerSameAsNumber(Integer number) {
494494
final Integer intValue = Integer.valueOf(this.value.toString());
495495
if (!intValue.equals(number)) {
496496
String validationErrorCode = "validation.msg." + this.resource + "." + this.parameter + ".not.equal.to.specified.number";
497-
String defaultEnglishMessage = "The parameter `" + this.parameter + "` must be same as" + number;
497+
String defaultEnglishMessage = "The parameter `" + this.parameter + "` must be same as " + number;
498498
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode, defaultEnglishMessage, this.parameter,
499499
intValue, number);
500500
this.dataValidationErrors.add(error);

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ private BigDecimal calculateFlatInterestRateForLoanTerm(final PaymentPeriodsInOn
10851085
final BigDecimal loanTermFrequencyBigDecimal = calculatePeriodsInLoanTerm();
10861086

10871087
return this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide(divisor, mc)
1088-
.multiply(loanTermFrequencyBigDecimal);
1088+
.multiply(loanTermFrequencyBigDecimal, mc);
10891089
}
10901090

10911091
private BigDecimal calculatePeriodsInLoanTerm() {
@@ -1237,7 +1237,7 @@ private Money calculateTotalPrincipalPerPeriodWithoutGrace(final MathContext mc,
12371237
}
12381238

12391239
if (this.installmentAmountInMultiplesOf != null) {
1240-
Money roundedPrincipalPerPeriod = Money.roundToMultiplesOf(principalPerPeriod, this.installmentAmountInMultiplesOf);
1240+
Money roundedPrincipalPerPeriod = Money.roundToMultiplesOf(principalPerPeriod, this.installmentAmountInMultiplesOf, mc);
12411241
if (interestForThisInstallment != null) {
12421242
Money roundedInterestForThisInstallment = Money.roundToMultiplesOf(interestForThisInstallment,
12431243
this.installmentAmountInMultiplesOf);

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanApplicationValidator.java

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,6 @@ public void validateForCreate(final Loan loan) {
214214
expectedFirstRepaymentOnDate);
215215
}
216216

217-
validateCumulativeMultiDisburse(loan);
218-
219217
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
220218
loan.getLoanProductRelatedDetail().getNumberOfRepayments(), loan.getLoanProductRelatedDetail().getRepayEvery(),
221219
loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
@@ -230,8 +228,6 @@ public void validateForModify(final Loan loan) {
230228
expectedFirstRepaymentOnDate);
231229
}
232230

233-
validateCumulativeMultiDisburse(loan);
234-
235231
validateLoanTermAndRepaidEveryValues(loan.getTermFrequency(), loan.getTermPeriodFrequencyType().getValue(),
236232
loan.getLoanProductRelatedDetail().getNumberOfRepayments(), loan.getLoanProductRelatedDetail().getRepayEvery(),
237233
loan.getLoanProductRelatedDetail().getRepaymentPeriodFrequencyType().getValue(), loan);
@@ -1742,16 +1738,8 @@ public void validateLoanMultiDisbursementDate(final JsonElement element, final D
17421738
if (transactionProcessingStrategyCode != null) {
17431739
final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed(LoanApiConstants.interestTypeParameterName,
17441740
element, Locale.getDefault());
1745-
String processorCode = loanRepaymentScheduleTransactionProcessorFactory
1746-
.determineProcessor(transactionProcessingStrategyCode).getCode();
1747-
boolean isProgressive = "advanced-payment-allocation-strategy".equals(processorCode);
1748-
if (isProgressive) {
1749-
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType)
1750-
.ignoreIfNull().inMinMaxRange(0, 1);
1751-
} else {
1752-
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType)
1753-
.ignoreIfNull().integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
1754-
}
1741+
baseDataValidator.reset().parameter(LoanApiConstants.interestTypeParameterName).value(interestType).ignoreIfNull()
1742+
.inMinMaxRange(0, 1);
17551743
}
17561744
} else {
17571745
if (loan.isCumulativeSchedule()) {
@@ -2208,19 +2196,6 @@ public void validateDisbursementDateWithMeetingDates(final LocalDate expectedDis
22082196
}
22092197
}
22102198

2211-
private static void validateCumulativeMultiDisburse(Loan loan) {
2212-
if (loan.isCumulativeSchedule() && loan.isMultiDisburmentLoan()
2213-
&& loan.getLoanProductRelatedDetail().getInterestMethod().isFlat()) {
2214-
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
2215-
final ApiParameterError error = ApiParameterError.generalError(
2216-
"validation.msg.loan.cumulative.multidisburse.does.not.support.flat.interest.mode",
2217-
"Cumulative multidisburse loan does NOT support FLAT interest mode.");
2218-
dataValidationErrors.add(error);
2219-
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
2220-
dataValidationErrors);
2221-
}
2222-
}
2223-
22242199
private Calendar getCalendarInstance(Loan loan) {
22252200
CalendarInstance calendarInstance = calendarInstanceRepository.findCalendarInstanceByEntityId(loan.getId(),
22262201
CalendarEntityType.LOANS.getValue());

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,13 +1059,7 @@ private void validateMultiDisburseLoanData(final DataValidatorBuilder baseDataVa
10591059

10601060
final Integer interestType = this.fromApiJsonHelper.extractIntegerNamed(INTEREST_TYPE, element, Locale.getDefault());
10611061

1062-
boolean isProgressive = isProgressive(element, loanProduct);
1063-
if (isProgressive) {
1064-
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull().inMinMaxRange(0, 1);
1065-
} else {
1066-
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull()
1067-
.integerSameAsNumber(InterestMethod.DECLINING_BALANCE.getValue());
1068-
}
1062+
baseDataValidator.reset().parameter(INTEREST_TYPE).value(interestType).ignoreIfNull().inMinMaxRange(0, 1);
10691063
}
10701064

10711065
final String overAppliedCalculationType = this.fromApiJsonHelper.extractStringNamed(OVER_APPLIED_CALCULATION_TYPE, element);

integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6251,6 +6251,75 @@ public void uc157() {
62516251
});
62526252
}
62536253

6254+
// UC158: Repayment schedule handling for flat cumulative multi-disbursement
6255+
@Test
6256+
public void uc158() {
6257+
AtomicLong loanIdRef = new AtomicLong();
6258+
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
6259+
final BigDecimal principalAmount = BigDecimal.valueOf(2000.0);
6260+
6261+
runAt("1 January 2024", () -> {
6262+
// Create a Cumulative Multidisbursal and Flat Interest Type
6263+
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(
6264+
createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct().interestType(InterestType.FLAT).daysInMonthType(30)//
6265+
.transactionProcessingStrategyCode(LoanProductTestBuilder.DEFAULT_STRATEGY).interestRateFrequencyType(YEARS)
6266+
.daysInYearType(365).loanScheduleType(LoanScheduleType.CUMULATIVE.toString()).repaymentEvery(1)
6267+
.installmentAmountInMultiplesOf(null)//
6268+
.repaymentFrequencyType(2L)//
6269+
);
6270+
assertNotNull(loanProductResponse.getResourceId());
6271+
6272+
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), "1 January 2024",
6273+
principalAmount.doubleValue(), 3).interestCalculationPeriodType(1).interestType(InterestType.FLAT)//
6274+
.transactionProcessingStrategyCode(LoanProductTestBuilder.DEFAULT_STRATEGY).interestRateFrequencyType(YEARS)//
6275+
.interestRatePerPeriod(BigDecimal.valueOf(7.0))//
6276+
.repaymentEvery(1)//
6277+
.repaymentFrequencyType(MONTHS)//
6278+
.loanTermFrequency(3)//
6279+
.loanTermFrequencyType(MONTHS);
6280+
PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest);
6281+
6282+
loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().approvedLoanAmount(principalAmount)
6283+
.dateFormat(DATETIME_PATTERN).approvedOnDate("1 January 2024").locale("en"));
6284+
6285+
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
6286+
new PostLoansLoanIdRequest().actualDisbursementDate("1 January 2024").dateFormat(DATETIME_PATTERN).locale("en")
6287+
.transactionAmount(BigDecimal.valueOf(1000.00)));
6288+
6289+
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
6290+
validateLoanSummaryBalances(loanDetails, 1017.50, 0.00, 1000.00, 0.00, null);
6291+
validatePeriod(loanDetails, 0, LocalDate.of(2024, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
6292+
null, null, null, null);
6293+
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,
6294+
5.83, 0.00, 5.83, 0.00, 0.00);
6295+
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,
6296+
5.83, 0.00, 5.83, 0.00, 0.00);
6297+
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,
6298+
5.84, 0.00, 5.84, 0.00, 0.00);
6299+
loanIdRef.set(loanResponse.getLoanId());
6300+
});
6301+
6302+
runAt("15 January 2024", () -> {
6303+
final Long loanId = loanIdRef.get();
6304+
6305+
loanTransactionHelper.disburseLoan(loanId, new PostLoansLoanIdRequest().actualDisbursementDate("15 January 2024")
6306+
.dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(BigDecimal.valueOf(500.0)));
6307+
6308+
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId);
6309+
validateLoanSummaryBalances(loanDetails, 1526.25, 0.00, 1500.00, 0.00, null);
6310+
validatePeriod(loanDetails, 0, LocalDate.of(2024, 1, 1), null, 1000.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
6311+
null, null, null, null);
6312+
validatePeriod(loanDetails, 1, LocalDate.of(2024, 1, 15), null, 500.0, null, null, null, 0.0, 0.0, null, null, null, null, null,
6313+
null, null, null, null);
6314+
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,
6315+
8.75, 0.00, 8.75, 0.00, 0.00);
6316+
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,
6317+
8.75, 0.00, 8.75, 0.00, 0.00);
6318+
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,
6319+
8.75, 0.00, 8.75, 0.00, 0.00);
6320+
});
6321+
}
6322+
62546323
private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId,
62556324
Integer numberOfRepayments, String loanDisbursementDate, double amount) {
62566325
LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");

0 commit comments

Comments
 (0)