diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java index 433fcf630c7..9fbd578f159 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/serialization/LoanProductDataValidator.java @@ -342,9 +342,9 @@ public void validateForCreate(final JsonCommand command) { // Validating whether the processor is existing loanRepaymentScheduleTransactionProcessorFactory.determineProcessor(transactionProcessingStrategyCode); + Long delinquencyBucketId = null; if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element)) { - final Long delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, - element); + delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element); baseDataValidator.reset().parameter(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME).value(delinquencyBucketId) .ignoreIfNull().integerGreaterThanZero(); } @@ -785,6 +785,13 @@ public void validateForCreate(final JsonCommand command) { .extractBooleanNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element); baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY) .value(enableInstallmentLevelDelinquency).ignoreIfNull().validateForBooleanValue(); + if (delinquencyBucketId == null) { + if (enableInstallmentLevelDelinquency) { + baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY).failWithCode( + "can.be.enabled.for.loan.product.having.valid.delinquency.bucket", + "Installment level delinquency cannot be enabled if Delinquency bucket is not configured for loan product"); + } + } } String loanScheduleType = LoanScheduleType.CUMULATIVE.name(); @@ -1301,9 +1308,9 @@ public void validateForUpdate(final JsonCommand command, final LoanProduct loanP .integerZeroOrGreater(); } + Long delinquencyBucketId = null; if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element)) { - final Long delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, - element); + delinquencyBucketId = this.fromApiJsonHelper.extractLongNamed(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME, element); baseDataValidator.reset().parameter(LoanProductConstants.DELINQUENCY_BUCKET_PARAM_NAME).value(delinquencyBucketId) .ignoreIfNull().integerGreaterThanZero(); } @@ -1770,6 +1777,13 @@ public void validateForUpdate(final JsonCommand command, final LoanProduct loanP .extractBooleanNamed(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY, element); baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY) .value(enableInstallmentLevelDelinquency).ignoreIfNull().validateForBooleanValue(); + if (delinquencyBucketId == null) { + if (enableInstallmentLevelDelinquency) { + baseDataValidator.reset().parameter(LoanProductConstants.ENABLE_INSTALLMENT_LEVEL_DELINQUENCY).failWithCode( + "can.be.enabled.for.loan.product.having.valid.delinquency.bucket", + "Installment level delinquency cannot be enabled if Delinquency bucket is not configured for loan product"); + } + } } String loanScheduleType = loanProduct.getLoanProductRelatedDetail().getLoanScheduleType().name(); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/CustomSnapshotEventIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/CustomSnapshotEventIntegrationTest.java index e8b9e335928..bfbb3242dcc 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/CustomSnapshotEventIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/CustomSnapshotEventIntegrationTest.java @@ -64,7 +64,6 @@ public void testSnapshotEventGenerationWhenLoanInstallmentIsNotPayed() { // Create Loan Product PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); - loanProductsRequest.setEnableInstallmentLevelDelinquency(true); PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); // Apply and Approve Loan @@ -112,7 +111,6 @@ public void testNoSnapshotEventGenerationWhenLoanInstallmentIsPayed() { // Create Loan Product PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); - loanProductsRequest.setEnableInstallmentLevelDelinquency(true); PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); // Apply and Approve Loan @@ -169,7 +167,6 @@ public void testNoSnapshotEventGenerationWhenWhenCustomSnapshotEventCOBTaskIsNot // Create Loan Product PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); - loanProductsRequest.setEnableInstallmentLevelDelinquency(true); PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); // Apply and Approve Loan @@ -215,7 +212,6 @@ public void testNoSnapshotEventGenerationWhenCOBDateIsNotMatchingWithInstallment // Create Loan Product PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); - loanProductsRequest.setEnableInstallmentLevelDelinquency(true); PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); // Apply and Approve Loan @@ -259,7 +255,6 @@ public void testNoSnapshotEventGenerationWhenCustomSnapshotEventIsDisabled() { // Create Loan Product PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); - loanProductsRequest.setEnableInstallmentLevelDelinquency(true); PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); // Apply and Approve Loan diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/InstallmentLevelDelinquencyAPIIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/InstallmentLevelDelinquencyAPIIntegrationTests.java index d6ed9adc7de..19a8e9c34e7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/InstallmentLevelDelinquencyAPIIntegrationTests.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/InstallmentLevelDelinquencyAPIIntegrationTests.java @@ -31,6 +31,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdResponse; import org.apache.fineract.client.models.PostLoanProductsRequest; import org.apache.fineract.client.models.PostLoanProductsResponse; +import org.apache.fineract.client.models.PutLoanProductsProductIdRequest; import org.apache.fineract.client.util.CallFailedRuntimeException; import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.SchedulerJobHelper; @@ -410,6 +411,55 @@ public void tesInstallmentLevelSettingForLoanWithLoanProductWithoutDelinquencyBu } + @Test + public void tesInstallmentLevelSettingForLoanProductWithoutDelinquencyBucketValidation() { + + runAt("31 May 2023", () -> { + // Create Client + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + + // Create Loan Product without delinquency bucket + PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( + InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); + // set installment level delinquency as true + loanProductsRequest.setEnableInstallmentLevelDelinquency(true); + + // Create loan product with installment level delinquency setting + CallFailedRuntimeException callFailedRuntimeException = Assertions.assertThrows(CallFailedRuntimeException.class, + () -> loanProductHelper.createLoanProduct(loanProductsRequest)); + + Assertions.assertTrue(callFailedRuntimeException.getMessage() + .contains("Installment level delinquency cannot be enabled if Delinquency bucket is not configured for loan product")); + + }); + + } + + @Test + public void tesUpdateInstallmentLevelSettingForLoanProductWithoutDelinquencyBucketValidation() { + + runAt("31 May 2023", () -> { + // Create Client + Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); + + // Create Loan Product without delinquency bucket + PostLoanProductsRequest loanProductsRequest = create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct( + InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS); + + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest); + + // Update loan product with installment level delinquency setting + CallFailedRuntimeException callFailedRuntimeException = Assertions.assertThrows(CallFailedRuntimeException.class, + () -> loanProductHelper.updateLoanProductById(loanProductResponse.getResourceId(), + new PutLoanProductsProductIdRequest().enableInstallmentLevelDelinquency(true).locale("en"))); + + Assertions.assertTrue(callFailedRuntimeException.getMessage() + .contains("Installment level delinquency cannot be enabled if Delinquency bucket is not configured for loan product")); + + }); + + } + private void updateBusinessDateAndExecuteCOBJob(String date) { businessDateHelper.updateBusinessDate( new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en"));