This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch release/1.13.1 in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 1e8fa1099c5269fd4ca6ccf36d74921f82cce49c Author: Oleksii Novikov <[email protected]> AuthorDate: Tue Oct 21 15:41:36 2025 +0300 FINERACT-2389: Fix the handling of nullable field overrides --- .../test/data/loanproduct/DefaultLoanProduct.java | 2 + .../global/LoanProductGlobalInitializerStep.java | 49 +++++++++ .../stepdef/loan/LoanOverrideFieldsStepDef.java | 120 +++++++++++++++++++++ .../fineract/test/support/TestContextKey.java | 2 + .../resources/features/LoanOverrideFileds.feature | 58 ++++++++++ .../loanaccount/api/LoansApiResourceSwagger.java | 10 ++ .../service/LoanScheduleAssembler.java | 30 +++--- .../integrationtests/BaseLoanIntegrationTest.java | 30 ++++-- .../DelinquencyActionIntegrationTests.java | 10 +- .../common/loans/LoanProductTestBuilder.java | 4 +- .../InitiateExternalAssetOwnerTransferTest.java | 4 +- 11 files changed, 290 insertions(+), 29 deletions(-) diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java index 1fe4813d90..851365f5a5 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/data/loanproduct/DefaultLoanProduct.java @@ -170,6 +170,8 @@ public enum DefaultLoanProduct implements LoanProduct { LP1_INTEREST_FLAT_DAILY_RECALCULATION_DAILY_360_30_MULTIDISB, // LP1_INTEREST_FLAT_SAR_RECALCULATION_SAME_AS_REPAYMENT_MULTIDISB_AUTO_DOWNPAYMENT, // LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY, // + LP1_WITH_OVERRIDES, // + LP1_NO_OVERRIDES // ; @Override diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java index 72f6100135..f9251b541b 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/initializer/global/LoanProductGlobalInitializerStep.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import lombok.RequiredArgsConstructor; import org.apache.fineract.client.models.AdvancedPaymentData; +import org.apache.fineract.client.models.AllowAttributeOverrides; import org.apache.fineract.client.models.CreditAllocationData; import org.apache.fineract.client.models.CreditAllocationOrder; import org.apache.fineract.client.models.LoanProductChargeData; @@ -4056,6 +4057,54 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ TestContext.INSTANCE.set( TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY, responseLoanProductsResponseAdvCustomPaymentAllocationProgressiveLoanInterestDailyEmi36030InterestRecalculationDaily); + + // (LP1_WITH_OVERRIDES) - Loan product with all attribute overrides ENABLED + final String nameWithOverrides = DefaultLoanProduct.LP1_WITH_OVERRIDES.getName(); + final PostLoanProductsRequest loanProductsRequestWithOverrides = loanProductsRequestFactory.defaultLoanProductsRequestLP1() // + .name(nameWithOverrides) // + .interestRatePerPeriod(1.0) // + .maxInterestRatePerPeriod(30.0) // + .inArrearsTolerance(10) // + .graceOnPrincipalPayment(1) // + .graceOnInterestPayment(1) // + .graceOnArrearsAgeing(3) // + .numberOfRepayments(6) // + .allowAttributeOverrides(new AllowAttributeOverrides() // + .amortizationType(true) // + .interestType(true) // + .transactionProcessingStrategyCode(true) // + .interestCalculationPeriodType(true) // + .inArrearsTolerance(true) // + .repaymentEvery(true) // + .graceOnPrincipalAndInterestPayment(true) // + .graceOnArrearsAgeing(true)); + final Response<PostLoanProductsResponse> responseWithOverrides = loanProductsApi.createLoanProduct(loanProductsRequestWithOverrides) + .execute(); + TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_WITH_OVERRIDES, responseWithOverrides); + + // (LP1_NO_OVERRIDES) - Loan product with all attribute overrides DISABLED + final String nameNoOverrides = DefaultLoanProduct.LP1_NO_OVERRIDES.getName(); + final PostLoanProductsRequest loanProductsRequestNoOverrides = loanProductsRequestFactory.defaultLoanProductsRequestLP1() // + .name(nameNoOverrides) // + .interestRatePerPeriod(1.0) // + .maxInterestRatePerPeriod(30.0) // + .inArrearsTolerance(10) // + .graceOnPrincipalPayment(1) // + .graceOnInterestPayment(1) // + .graceOnArrearsAgeing(3) // + .numberOfRepayments(6) // + .allowAttributeOverrides(new AllowAttributeOverrides() // + .amortizationType(false) // + .interestType(false) // + .transactionProcessingStrategyCode(false) // + .interestCalculationPeriodType(false) // + .inArrearsTolerance(false) // + .repaymentEvery(false) // + .graceOnPrincipalAndInterestPayment(false) // + .graceOnArrearsAgeing(false)); + final Response<PostLoanProductsResponse> responseNoOverrides = loanProductsApi.createLoanProduct(loanProductsRequestNoOverrides) + .execute(); + TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_NO_OVERRIDES, responseNoOverrides); } public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule, diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanOverrideFieldsStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanOverrideFieldsStepDef.java new file mode 100644 index 0000000000..4f8185c9bb --- /dev/null +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanOverrideFieldsStepDef.java @@ -0,0 +1,120 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.test.stepdef.loan; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import io.cucumber.datatable.DataTable; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.apache.fineract.client.models.GetLoansLoanIdResponse; +import org.apache.fineract.client.models.PostClientsResponse; +import org.apache.fineract.client.models.PostLoansRequest; +import org.apache.fineract.client.models.PostLoansResponse; +import org.apache.fineract.client.services.LoansApi; +import org.apache.fineract.test.data.loanproduct.DefaultLoanProduct; +import org.apache.fineract.test.data.loanproduct.LoanProductResolver; +import org.apache.fineract.test.factory.LoanRequestFactory; +import org.apache.fineract.test.helper.ErrorHelper; +import org.apache.fineract.test.stepdef.AbstractStepDef; +import org.apache.fineract.test.support.TestContextKey; +import retrofit2.Response; + +@RequiredArgsConstructor +public class LoanOverrideFieldsStepDef extends AbstractStepDef { + + private final LoanRequestFactory loanRequestFactory; + private final LoanProductResolver loanProductResolver; + private final LoansApi loansApi; + + @Then("LoanDetails has {string} field with value: {string}") + public void checkLoanDetailsFieldWithValue(final String fieldName, final String expectedValue) throws IOException { + final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE); + assertNotNull(loanResponse.body()); + final Long loanId = loanResponse.body().getLoanId(); + + final Response<GetLoansLoanIdResponse> loanDetails = loansApi.retrieveLoan(loanId, false, "", "", "").execute(); + ErrorHelper.checkSuccessfulApiCall(loanDetails); + assertNotNull(loanDetails.body()); + + verifyFieldValue(loanDetails.body(), fieldName, expectedValue); + } + + private void verifyFieldValue(final GetLoansLoanIdResponse loanDetails, final String fieldName, final String expectedValue) { + final Integer actualValue = getIntFieldValue(loanDetails, fieldName); + final Integer expected = Integer.valueOf(expectedValue); + assertThat(actualValue).as("Expected %s to be %d but was %s", fieldName, expected, actualValue).isEqualTo(expected); + } + + private Integer getIntFieldValue(final GetLoansLoanIdResponse loanDetails, final String fieldName) { + return switch (fieldName) { + case "inArrearsTolerance" -> loanDetails.getInArrearsTolerance(); + case "graceOnPrincipalPayment" -> loanDetails.getGraceOnPrincipalPayment(); + case "graceOnInterestPayment" -> loanDetails.getGraceOnInterestPayment(); + case "graceOnArrearsAgeing" -> loanDetails.getGraceOnArrearsAgeing(); + default -> throw new IllegalArgumentException("Unknown override field: " + fieldName); + }; + } + + @When("Admin creates a new Loan with the following override data:") + public void createLoanWithOverrideData(final DataTable dataTable) throws IOException { + final Response<PostClientsResponse> clientResponse = testContext().get(TestContextKey.CLIENT_CREATE_RESPONSE); + assertNotNull(clientResponse.body()); + final Long clientId = clientResponse.body().getClientId(); + + final Map<String, String> overrideData = dataTable.asMap(String.class, String.class); + + final String loanProductName = overrideData.get("loanProduct"); + if (loanProductName == null) { + throw new IllegalArgumentException("loanProduct is required in override data"); + } + + final PostLoansRequest loansRequest = loanRequestFactory.defaultLoansRequest(clientId) + .productId(loanProductResolver.resolve(DefaultLoanProduct.valueOf(loanProductName))).numberOfRepayments(6) + .loanTermFrequency(180).interestRatePerPeriod(new BigDecimal(1)); + + overrideData.forEach((fieldName, value) -> { + if (!"loanProduct".equals(fieldName)) { + applyOverrideField(loansRequest, fieldName, value); + } + }); + + final Response<PostLoansResponse> response = loansApi.calculateLoanScheduleOrSubmitLoanApplication(loansRequest, "").execute(); + testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response); + ErrorHelper.checkSuccessfulApiCall(response); + } + + private void applyOverrideField(final PostLoansRequest request, final String fieldName, final String value) { + final boolean isNull = "null".equals(value); + + switch (fieldName) { + case "inArrearsTolerance" -> request.inArrearsTolerance(isNull ? null : new BigDecimal(value)); + case "graceOnInterestPayment" -> request.graceOnInterestPayment(isNull ? null : Integer.valueOf(value)); + case "graceOnPrincipalPayment" -> request.graceOnPrincipalPayment(isNull ? null : Integer.valueOf(value)); + case "graceOnArrearsAgeing" -> request.graceOnArrearsAgeing(isNull ? null : Integer.valueOf(value)); + default -> throw new IllegalArgumentException("Unknown override field: " + fieldName); + } + } + +} diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java index 37c72d2d34..0f56c55464 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/support/TestContextKey.java @@ -275,4 +275,6 @@ public abstract class TestContextKey { public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_INTEREST_RECALC_360_30_MULTIDISB_OVER_APPLIED_EXPECTED_TRANCHES = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentInterestRecalculationMultidisbursalApprovedOverAppliedAmountExpectedTransches"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALCULATION_DAILY_TILL_PRECLOSE_MIN_INT_3_MAX_INT_20 = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyTillPreCloseMinInt3MaxInt20"; public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_WRITE_OFF_REASON_MAP = "loanProductCreateResponseLP2ProgressiveAdvancedPaymentWriteOffReasonMap"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_WITH_OVERRIDES = "loanProductCreateResponseLP1WithOverrides"; + public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_NO_OVERRIDES = "loanProductCreateResponseLP1NoOverrides"; } diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanOverrideFileds.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanOverrideFileds.feature new file mode 100644 index 0000000000..b955d0f272 --- /dev/null +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanOverrideFileds.feature @@ -0,0 +1,58 @@ +@LoanOverrideFields +Feature: LoanOverrideFields + + Scenario: Verify that all nullable fields default to product when overrides not allowed and not provided + When Admin sets the business date to the actual date + When Admin creates a client with random data + When Admin creates a new Loan with the following override data: + | loanProduct | LP1_NO_OVERRIDES | + | inArrearsTolerance | null | + | graceOnPrincipalPayment | null | + | graceOnInterestPayment | null | + | graceOnArrearsAgeing | null | + Then LoanDetails has "inArrearsTolerance" field with value: "10" + Then LoanDetails has "graceOnPrincipalPayment" field with value: "1" + Then LoanDetails has "graceOnInterestPayment" field with value: "1" + Then LoanDetails has "graceOnArrearsAgeing" field with value: "3" + + Scenario: Verify that all nullable fields ignore overrides when overrides not allowed + When Admin sets the business date to the actual date + When Admin creates a client with random data + When Admin creates a new Loan with the following override data: + | loanProduct | LP1_NO_OVERRIDES | + | inArrearsTolerance | 11 | + | graceOnPrincipalPayment | 2 | + | graceOnInterestPayment | 2 | + | graceOnArrearsAgeing | 4 | + Then LoanDetails has "inArrearsTolerance" field with value: "10" + Then LoanDetails has "graceOnPrincipalPayment" field with value: "1" + Then LoanDetails has "graceOnInterestPayment" field with value: "1" + Then LoanDetails has "graceOnArrearsAgeing" field with value: "3" + + Scenario: Verify that nullable fields default to product when override is allowed but not provided + When Admin sets the business date to the actual date + When Admin creates a client with random data + When Admin creates a new Loan with the following override data: + | loanProduct | LP1_WITH_OVERRIDES | + | inArrearsTolerance | null | + | graceOnPrincipalPayment | null | + | graceOnInterestPayment | null | + | graceOnArrearsAgeing | null | + Then LoanDetails has "inArrearsTolerance" field with value: "10" + Then LoanDetails has "graceOnPrincipalPayment" field with value: "1" + Then LoanDetails has "graceOnInterestPayment" field with value: "1" + Then LoanDetails has "graceOnArrearsAgeing" field with value: "3" + + Scenario: Verify that nullable fields default to product when override is allowed and provided + When Admin sets the business date to the actual date + When Admin creates a client with random data + When Admin creates a new Loan with the following override data: + | loanProduct | LP1_WITH_OVERRIDES | + | inArrearsTolerance | 11 | + | graceOnPrincipalPayment | 2 | + | graceOnInterestPayment | 2 | + | graceOnArrearsAgeing | 4 | + Then LoanDetails has "inArrearsTolerance" field with value: "11" + Then LoanDetails has "graceOnPrincipalPayment" field with value: "2" + Then LoanDetails has "graceOnInterestPayment" field with value: "2" + Then LoanDetails has "graceOnArrearsAgeing" field with value: "4" diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index 3ca327b710..40d97e3fc4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -1216,6 +1216,14 @@ final class LoansApiResourceSwagger { public Boolean chargedOff; @Schema(example = "3") public Integer inArrearsTolerance; + @Schema(example = "0") + public Integer graceOnPrincipalPayment; + @Schema(example = "0") + public Integer graceOnInterestPayment; + @Schema(example = "0") + public Integer graceOnInterestCharged; + @Schema(example = "3") + public Integer graceOnArrearsAgeing; @Schema(example = "false") public Boolean enableDownPayment; @Schema(example = "0.000000") @@ -1348,6 +1356,8 @@ final class LoansApiResourceSwagger { public Integer graceOnInterestPayment; @Schema(example = "1") public Integer graceOnArrearsAgeing; + @Schema(example = "10") + public BigDecimal inArrearsTolerance; @Schema(example = "HORIZONTAL") public String loanScheduleProcessingType; @Schema(example = "false") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index c0742533d9..bb303f52b6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -350,27 +350,33 @@ public class LoanScheduleAssembler { } // grace details - final Integer graceOnPrincipalPayment = allowOverridingGraceOnPrincipalAndInterestPayment - ? this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element) - : loanProduct.getLoanProductRelatedDetail().getGraceOnPrincipalPayment(); + Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element); + if (!allowOverridingGraceOnPrincipalAndInterestPayment || graceOnPrincipalPayment == null) { + graceOnPrincipalPayment = loanProduct.getLoanProductRelatedDetail().getGraceOnPrincipalPayment(); + } final Integer recurringMoratoriumOnPrincipalPeriods = this.fromApiJsonHelper .extractIntegerWithLocaleNamed("recurringMoratoriumOnPrincipalPeriods", element); - final Integer graceOnInterestPayment = allowOverridingGraceOnPrincipalAndInterestPayment - ? this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element) - : loanProduct.getLoanProductRelatedDetail().getGraceOnInterestPayment(); + Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element); + if (!allowOverridingGraceOnPrincipalAndInterestPayment || graceOnInterestPayment == null) { + graceOnInterestPayment = loanProduct.getLoanProductRelatedDetail().getGraceOnInterestPayment(); + } final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element); final LocalDate interestChargedFromDate = this.fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element); final Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled = this.configurationDomainService .isInterestChargedFromDateSameAsDisbursementDate(); - final Integer graceOnArrearsAgeing = allowOverridingGraceOnArrearsAging - ? this.fromApiJsonHelper.extractIntegerWithLocaleNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element) - : loanProduct.getLoanProductRelatedDetail().getGraceOnArrearsAgeing(); + Integer graceOnArrearsAgeing = this.fromApiJsonHelper + .extractIntegerWithLocaleNamed(LoanProductConstants.GRACE_ON_ARREARS_AGEING_PARAMETER_NAME, element); + if (!allowOverridingGraceOnArrearsAging || graceOnArrearsAgeing == null) { + graceOnArrearsAgeing = loanProduct.getLoanProductRelatedDetail().getGraceOnArrearsAgeing(); + } // other - final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element); - final Money inArrearsToleranceMoney = allowOverridingArrearsTolerance ? Money.of(currency, inArrearsTolerance) - : loanProduct.getLoanProductRelatedDetail().getInArrearsTolerance(); + BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element); + if (!allowOverridingArrearsTolerance || inArrearsTolerance == null) { + inArrearsTolerance = loanProduct.getLoanProductRelatedDetail().getInArrearsTolerance().getAmount(); + } + final Money inArrearsToleranceMoney = Money.of(currency, inArrearsTolerance); final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.fixedEmiAmountParameterName, element); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java index ba07af2d60..08bacb5170 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java @@ -886,7 +886,8 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest { .repaymentEvery(1)// .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue())// .interestType(interestType)// - .amortizationType(amortizationType); + .amortizationType(amortizationType)// + .graceOnArrearsAgeing(0); } private RequestSpecification createRequestSpecification(String authKey) { @@ -1350,13 +1351,26 @@ public abstract class BaseLoanIntegrationTest extends IntegrationTest { protected PostLoansRequest applyLoanRequest(Long clientId, Long loanProductId, String loanDisbursementDate, Double amount, int numberOfRepayments, Consumer<PostLoansRequest> customizer) { - PostLoansRequest postLoansRequest = new PostLoansRequest().clientId(clientId).productId(loanProductId) - .expectedDisbursementDate(loanDisbursementDate).dateFormat(DATETIME_PATTERN) - .transactionProcessingStrategyCode(DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY) - .locale("en").submittedOnDate(loanDisbursementDate).amortizationType(1).interestRatePerPeriod(BigDecimal.ZERO) - .interestCalculationPeriodType(1).interestType(0).repaymentEvery(30).repaymentFrequencyType(0) - .numberOfRepayments(numberOfRepayments).loanTermFrequency(numberOfRepayments * 30).loanTermFrequencyType(0) - .maxOutstandingLoanBalance(BigDecimal.valueOf(amount)).principal(BigDecimal.valueOf(amount)).loanType("individual"); + PostLoansRequest postLoansRequest = new PostLoansRequest().clientId(clientId) // + .productId(loanProductId) // + .expectedDisbursementDate(loanDisbursementDate) // + .dateFormat(DATETIME_PATTERN) // + .transactionProcessingStrategyCode(DUE_PENALTY_INTEREST_PRINCIPAL_FEE_IN_ADVANCE_PENALTY_INTEREST_PRINCIPAL_FEE_STRATEGY) // + .locale("en") // + .submittedOnDate(loanDisbursementDate) // + .amortizationType(1) // + .interestRatePerPeriod(BigDecimal.ZERO) // + .interestCalculationPeriodType(1) // + .interestType(0) // + .repaymentEvery(30) // + .repaymentFrequencyType(0) // + .numberOfRepayments(numberOfRepayments) // + .loanTermFrequency(numberOfRepayments * 30) // + .loanTermFrequencyType(0) // + .maxOutstandingLoanBalance(BigDecimal.valueOf(amount)) // + .principal(BigDecimal.valueOf(amount)) // + .loanType("individual") // + .graceOnArrearsAgeing(0); if (customizer != null) { customizer.accept(postLoansRequest); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java index 168a029c8b..12be1252a1 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyActionIntegrationTests.java @@ -285,15 +285,15 @@ public class DelinquencyActionIntegrationTests extends BaseLoanIntegrationTest { verifyLoanDelinquencyData(loanId, 6, new InstallmentDelinquencyData(4, 10, BigDecimal.valueOf(250.0))); // Create Delinquency Pause for the Loan in the past - PostLoansDelinquencyActionResponse response = loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, - "27 January 2023", "15 February 2023"); + loanTransactionHelper.createLoanDelinquencyAction(loanId, PAUSE, "27 January 2023", "15 February 2023"); List<GetDelinquencyActionsResponse> loanDelinquencyActions = loanTransactionHelper.getLoanDelinquencyActions(loanId); Assertions.assertNotNull(loanDelinquencyActions); Assertions.assertEquals(1, loanDelinquencyActions.size()); - Assertions.assertEquals("PAUSE", loanDelinquencyActions.get(0).getAction()); - Assertions.assertEquals(LocalDate.parse("27 January 2023", dateTimeFormatter), loanDelinquencyActions.get(0).getStartDate()); - Assertions.assertEquals(LocalDate.parse("15 February 2023", dateTimeFormatter), loanDelinquencyActions.get(0).getEndDate()); + Assertions.assertEquals("PAUSE", loanDelinquencyActions.getFirst().getAction()); + Assertions.assertEquals(LocalDate.parse("27 January 2023", dateTimeFormatter), + loanDelinquencyActions.getFirst().getStartDate()); + Assertions.assertEquals(LocalDate.parse("15 February 2023", dateTimeFormatter), loanDelinquencyActions.getFirst().getEndDate()); // Loan delinquency data calculation after backdated pause verifyLoanDelinquencyData(loanId, 3, new InstallmentDelinquencyData(1, 3, BigDecimal.valueOf(250.0))); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java index 0ae33fa693..8ae4bcc1df 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanProductTestBuilder.java @@ -137,8 +137,8 @@ public class LoanProductTestBuilder { private String minimumGuaranteeFromOwnFunds = null; private String minimumGuaranteeFromGuarantor = null; private String isArrearsBasedOnOriginalSchedule = null; - private String graceOnPrincipalPayment = "1"; - private String graceOnInterestPayment = "1"; + private String graceOnPrincipalPayment = null; + private String graceOnInterestPayment = null; private JsonObject allowAttributeOverrides = null; private Boolean allowPartialPeriodInterestCalcualtion = false; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java index 7848cf6adf..4b7f3f92c3 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java @@ -1368,8 +1368,8 @@ public class InitiateExternalAssetOwnerTransferTest extends BaseLoanIntegrationT .withLoanTermFrequencyAsMonths().withNumberOfRepayments("4").withRepaymentEveryAfter("1") .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("2").withAmortizationTypeAsEqualInstallments() .withInterestTypeAsDecliningBalance().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() - .withExpectedDisbursementDate(date).withSubmittedOnDate(date).withCollaterals(collaterals) - .build(clientID, loanProductID, null); + .withExpectedDisbursementDate(date).withSubmittedOnDate(date).withCollaterals(collaterals).withInArrearsTolerance("0") + .withPrincipalGrace("0").withInterestGrace("0").build(clientID, loanProductID, null); return LOAN_TRANSACTION_HELPER.getLoanId(loanApplicationJSON); }
