FINERACT-2354: handle reamortization error code 500 issue on same disbursement day

This commit is contained in:
Attila Budai
2026-01-15 10:17:43 +01:00
committed by Adam Saghy
parent 53ff74425c
commit 442290ef92
6 changed files with 219 additions and 54 deletions
@@ -105,6 +105,31 @@ public class LoanReAmortizationStepDef extends AbstractStepDef {
testContext().set(TestContextKey.LOAN_REAMORTIZATION_UNDO_RESPONSE, response);
}
@When("Admin creates a Loan re-amortization transaction on current business date but fails with {int} error")
public void createLoanReAmortizationFailsWithError(int errorCode) {
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final Long loanId = loanResponse.getLoanId();
final PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest();
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
reAmortizationRequest, Map.of("command", "reAmortize")));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(errorCode);
}
@When("Admin creates a Loan re-amortization transaction on current business date with reAmortizationInterestHandling {string} but fails with {int} error")
public void createLoanReAmortizationWithInterestHandlingFailsWithError(final String reAmortizationInterestHandling, int errorCode) {
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final Long loanId = loanResponse.getLoanId();
final PostLoansLoanIdTransactionsRequest reAmortizationRequest = LoanRequestFactory.defaultLoanReAmortizationRequest()
.reAmortizationInterestHandling(reAmortizationInterestHandling);
CallFailedRuntimeException exception = fail(() -> fineractClient.loanTransactions().executeLoanTransaction(loanId,
reAmortizationRequest, Map.of("command", "reAmortize")));
assertThat(exception.getStatus()).as(ErrorMessageHelper.dateFailureErrorCodeMsg()).isEqualTo(errorCode);
}
@When("Admin creates a Loan re-amortization transaction on current business date is forbidden as loan was charged-off")
public void reAmortizationChargedOffLoanFailure() {
reAmortizationFailure(ErrorMessageHelper.reAmortizeChargedOffLoanFailure());
@@ -170,6 +195,20 @@ public class LoanReAmortizationStepDef extends AbstractStepDef {
testContext().set(TestContextKey.LOAN_REAMORTIZATION_PREVIEW_RESPONSE, response);
}
@When("Admin creates a Loan re-amortization preview by Loan external ID with the following data, but fails with {int} error code:")
public void createReAmortizedPreviewByLoanExternalIdFailsWithErrorCode(int errorCode, final DataTable table) {
final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final String loanExternalId = loanResponse.getResourceExternalId();
final List<String> data = table.asLists().get(1);
final String reAmortizationInterestHandling = data.getFirst();
final Map<String, Object> queryParams = Map.of("reAmortizationInterestHandling", reAmortizationInterestHandling);
CallFailedRuntimeException exception = fail(
() -> fineractClient.loanTransactions().previewReAmortizationSchedule1(loanExternalId, queryParams));
assertThat(exception.getStatus()).isEqualTo(errorCode);
}
@Then("Loan Re-Amortization Repayment schedule preview has the following data in Total row:")
public void loanRepaymentDScheduleAmountCheck(final DataTable table) {
final List<List<String>> data = table.asLists();
@@ -5101,9 +5101,8 @@ Feature: LoanReAmortization
When Loan Pay-off is made on "02 April 2024"
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
@Skip
@TestRailId:C4510 @AdvancedPaymentAllocation
Scenario: Verify allowing re-amortization on same day as disbursement - interest bearing loan with default interest handling - UC5.1
Scenario: Verify re-amortization on same day as disbursement is rejected when no overdue exists - interest bearing loan with default interest handling - UC5.1
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
@@ -5127,31 +5126,14 @@ Feature: LoanReAmortization
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
# --- re-amortization transaction --- 500 error!!
And Admin creates a Loan re-amortization transaction on current business date
Then Loan Repayment schedule has 6 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
| 15 March 2024 | Re-amortize | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
# --- re-amortization transaction - fails with 403 because no overdue installments exist on disbursement day ---
And Admin creates a Loan re-amortization transaction on current business date but fails with 403 error
# --- close the loan --- #
When Loan Pay-off is made on "01 January 2024"
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
@Skip
@TestRailId:C4511 @AdvancedPaymentAllocation
Scenario: Verify allowing re-amortization on same day as disbursement - interest bearing loan with equal amortization + interest split - UC5.2
Scenario: Verify re-amortization on same day as disbursement is rejected when no overdue exists - interest bearing loan with equal amortization + interest split - UC5.2
When Admin sets the business date to "01 January 2024"
When Admin creates a client with random data
When Admin set "LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL" loan product "DEFAULT" transaction type to "NEXT_INSTALLMENT" future installment allocation rule
@@ -5175,24 +5157,8 @@ Feature: LoanReAmortization
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false |
# --- re-amortization transaction --- 500 error!!
And Admin creates a Loan re-amortization transaction on current business date with reAmortizationInterestHandling "EQUAL_AMORTIZATION_INTEREST_SPLIT"
Then Loan Repayment schedule has 6 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.05 | 0.0 | 0.0 | 102.05 | 0.0 | 0.0 | 0.0 | 102.05 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed |
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
| 15 March 2024 | Re-amortize | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 0.0 | false | false |
# --- re-amortization transaction - fails with 403 because no overdue installments exist on disbursement day ---
And Admin creates a Loan re-amortization transaction on current business date with reAmortizationInterestHandling "EQUAL_AMORTIZATION_INTEREST_SPLIT" but fails with 403 error
# --- close the loan --- #
When Loan Pay-off is made on "01 January 2024"
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
@@ -705,21 +705,9 @@ Feature: LoanReAmortizationPreview
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 | false | false |
| 01 March 2024 | Repayment | 17.01 | 16.52 | 0.49 | 0.0 | 0.0 | 67.05 | false | false |
# --- Re-amortization repayment schedule preview ---
And Admin creates a Loan re-amortization preview by Loan external ID with the following data:
# --- Re-amortization repayment schedule preview - should fail because no overdue principal exists ---
And Admin creates a Loan re-amortization preview by Loan external ID with the following data, but fails with 403 error code:
| reAmortizationInterestHandling |
| DEFAULT |
Then Loan Re-Amortization Repayment schedule preview has 6 periods, with the following data for periods:
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 01 February 2024 | 83.57 | 16.43 | 0.58 | 0.0 | 0.0 | 17.01 | 17.01 | 0.0 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | | 67.05 | 16.52 | 0.49 | 0.0 | 10.0 | 27.01 | 17.01 | 0.0 | 0.0 | 10.0 |
| 3 | 31 | 01 April 2024 | | 50.43 | 16.62 | 0.39 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 4 | 30 | 01 May 2024 | | 33.71 | 16.72 | 0.29 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 5 | 31 | 01 June 2024 | | 16.9 | 16.81 | 0.2 | 0.0 | 0.0 | 17.01 | 0.0 | 0.0 | 0.0 | 17.01 |
| 6 | 30 | 01 July 2024 | | 0.0 | 16.9 | 0.1 | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 0.0 | 17.0 |
And Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 100.0 | 2.05 | 0.0 | 10.0 | 112.05 | 34.02 | 0.0 | 0.0 | 78.03 |
When Loan Pay-off is made on "15 March 2024"
Then Loan is closed with zero outstanding balance and it's all installments have obligations met
@@ -131,6 +131,15 @@ public class LoanReAmortizationValidator {
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.not.allowed.on.contract.terminated",
"Loan re-amortization is not allowed on contract terminated loan.", loan.getId());
}
// validate there are overdue installments to re-amortize
boolean hasOverdueInstallments = loan.getRepaymentScheduleInstallments().stream()
.anyMatch(installment -> installment.getDueDate().isBefore(getBusinessLocalDate())
&& installment.getPrincipalOutstanding(loan.getCurrency()).isGreaterThanZero());
if (!hasOverdueInstallments) {
throw new GeneralPlatformDomainRuleException("error.msg.loan.reamortize.no.overdue.amount",
"Re-amortization cannot be executed: no overdue amount exists.", loan.getId());
}
}
public LoanTransaction findAndValidateReAmortizeTransactionForUndo(Loan loan) {
@@ -42,7 +42,10 @@ import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRu
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
@@ -179,6 +182,19 @@ class LoanReAmortizationValidatorTest {
.isEqualTo("error.msg.loan.reamortize.reamortize.transaction.already.present.for.today");
}
@Test
public void testValidateReAmortize_ShouldThrowException_WhenNoOverdueInstallmentsExist() {
// given
Loan loan = loan(false);
JsonCommand command = jsonCommand();
// when
GeneralPlatformDomainRuleException result = assertThrows(GeneralPlatformDomainRuleException.class,
() -> underTest.validateReAmortize(loan, command));
// then
assertThat(result).isNotNull();
assertThat(result.getGlobalisationMessageCode()).isEqualTo("error.msg.loan.reamortize.no.overdue.amount");
}
@Test
public void testValidateUndoReAmortize_ShouldThrowException_WhenLoanDoesntHaveReAmortization() {
// given
@@ -238,6 +254,10 @@ class LoanReAmortizationValidatorTest {
}
private Loan loan() {
return loan(true);
}
private Loan loan(boolean withOverdueInstallments) {
Loan loan = mock(Loan.class);
given(loan.getStatus()).willReturn(LoanStatus.ACTIVE);
given(loan.getMaturityDate()).willReturn(actualDate.plusDays(30));
@@ -248,6 +268,24 @@ class LoanReAmortizationValidatorTest {
given(loanProductRelatedDetail.getLoanScheduleType()).willReturn(LoanScheduleType.PROGRESSIVE);
given(loan.isInterestBearing()).willReturn(false);
given(loan.getLoanTransactions()).willReturn(List.of());
MonetaryCurrency currency = new MonetaryCurrency("USD", 2, null);
given(loan.getCurrency()).willReturn(currency);
Money principalOutstanding = mock(Money.class);
given(principalOutstanding.isGreaterThanZero()).willReturn(true);
if (withOverdueInstallments) {
LoanRepaymentScheduleInstallment overdueInstallment = mock(LoanRepaymentScheduleInstallment.class);
given(overdueInstallment.getDueDate()).willReturn(actualDate.minusDays(5));
given(overdueInstallment.getPrincipalOutstanding(currency)).willReturn(principalOutstanding);
given(loan.getRepaymentScheduleInstallments()).willReturn(List.of(overdueInstallment));
} else {
LoanRepaymentScheduleInstallment futureInstallment = mock(LoanRepaymentScheduleInstallment.class);
given(futureInstallment.getDueDate()).willReturn(actualDate.plusDays(10));
given(futureInstallment.getPrincipalOutstanding(currency)).willReturn(principalOutstanding);
given(loan.getRepaymentScheduleInstallments()).willReturn(List.of(futureInstallment));
}
return loan;
}
@@ -18,7 +18,10 @@
*/
package org.apache.fineract.integrationtests.loan.reamortization;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicLong;
@@ -27,6 +30,7 @@ import org.apache.fineract.client.models.PostLoanProductsRequest;
import org.apache.fineract.client.models.PostLoanProductsResponse;
import org.apache.fineract.client.models.PostLoansRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.client.util.CallFailedRuntimeException;
import org.apache.fineract.integrationtests.BaseLoanIntegrationTest;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
@@ -894,6 +898,127 @@ public class LoanReAmortizationIntegrationTest extends BaseLoanIntegrationTest {
}
@Test
public void reAmortizationOnDisbursementDayInterestBearingLoanTest() {
runAt("01 January 2024", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
int numberOfRepayments = 6;
int repaymentEvery = 1;
// Create Interest-Bearing Loan Product with 7% interest (progressive schedule)
Long loanProductId = createInterestBearingProgressiveLoanProduct(numberOfRepayments, repaymentEvery);
// Apply for loan with 200 amount
double applyAmount = 200.0;
double approveAmount = 100.0;
double disburseAmount = 100.0;
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2024", applyAmount,
numberOfRepayments)//
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
.repaymentEvery(repaymentEvery)//
.loanTermFrequency(numberOfRepayments)//
.repaymentFrequencyType(RepaymentFrequencyType.MONTHS)//
.loanTermFrequencyType(RepaymentFrequencyType.MONTHS)//
.interestRatePerPeriod(BigDecimal.valueOf(7.0))//
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY);
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest);
loanId.set(postLoansResponse.getLoanId());
// Approve with 100 (partial approval)
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(approveAmount, "01 January 2024"));
// Disburse 100 on Jan 1, 2024
disburseLoan(loanId.get(), BigDecimal.valueOf(disburseAmount), "01 January 2024");
// Verify disbursement transaction exists
verifyTransactions(loanId.get(), //
transaction(100.0, "Disbursement", "01 January 2024") //
);
// Re-amortize loan on the same day as disbursement should throw exception
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
() -> reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.DEFAULT.name()));
assertEquals(403, exception.getResponse().code());
assertTrue(exception.getMessage().contains("error.msg.loan.reamortize.no.overdue.amount"));
});
}
@Test
public void reAmortizationOnDisbursementDayEqualInterestSplitTest() {
runAt("01 January 2024", () -> {
// Create Client
Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
int numberOfRepayments = 6;
int repaymentEvery = 1;
// Create Interest-Bearing Loan Product with 7% interest (progressive schedule)
Long loanProductId = createInterestBearingProgressiveLoanProduct(numberOfRepayments, repaymentEvery);
// Apply for loan with 200 amount
double applyAmount = 200.0;
double approveAmount = 100.0;
double disburseAmount = 100.0;
PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductId, "01 January 2024", applyAmount,
numberOfRepayments)//
.transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)//
.repaymentEvery(repaymentEvery)//
.loanTermFrequency(numberOfRepayments)//
.repaymentFrequencyType(RepaymentFrequencyType.MONTHS)//
.loanTermFrequencyType(RepaymentFrequencyType.MONTHS)//
.interestRatePerPeriod(BigDecimal.valueOf(7.0))//
.interestCalculationPeriodType(InterestCalculationPeriodType.DAILY);
PostLoansResponse postLoansResponse = loanTransactionHelper.applyLoan(applicationRequest);
loanId.set(postLoansResponse.getLoanId());
// Approve with 100 (partial approval)
loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(), approveLoanRequest(approveAmount, "01 January 2024"));
// Disburse 100 on Jan 1, 2024
disburseLoan(loanId.get(), BigDecimal.valueOf(disburseAmount), "01 January 2024");
// Verify disbursement transaction exists
verifyTransactions(loanId.get(), //
transaction(100.0, "Disbursement", "01 January 2024") //
);
// Re-amortize loan on the same day as disbursement with EQUAL interest split should throw exception
CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
() -> reAmortizeLoan(loanId.get(), LoanReAmortizationInterestHandlingType.EQUAL_AMORTIZATION_INTEREST_SPLIT.name()));
assertEquals(403, exception.getResponse().code());
assertTrue(exception.getMessage().contains("error.msg.loan.reamortize.no.overdue.amount"));
});
}
private Long createInterestBearingProgressiveLoanProduct(int numberOfRepayments, int repaymentEvery) {
PostLoanProductsRequest product = create4IProgressive()//
.numberOfRepayments(numberOfRepayments)//
.repaymentEvery(repaymentEvery)//
.repaymentFrequencyType(RepaymentFrequencyType.MONTHS_L)//
.interestRatePerPeriod(7.0)// 7% annual interest
.multiDisburseLoan(true)//
.maxTrancheCount(10)//
.outstandingLoanBalance(10000.0)//
.disallowExpectedDisbursements(true)//
.allowApprovedDisbursedAmountsOverApplied(true)//
.overAppliedCalculationType("percentage")//
.overAppliedNumber(50);
PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product);
GetLoanProductsProductIdResponse getLoanProductsProductIdResponse = loanProductHelper
.retrieveLoanProductById(loanProductResponse.getResourceId());
assertNotNull(getLoanProductsProductIdResponse);
return loanProductResponse.getResourceId();
}
private Long createLoanProductWithMultiDisbursalAndRepaymentsWithEnableDownPayment(int numberOfInstallments, int repaymentEvery) {
return createLoanProductWithMultiDisbursalAndRepaymentsWithEnableDownPayment(numberOfInstallments, repaymentEvery,
DOWN_PAYMENT_PERCENTAGE);