From 6dbbaca3dadbc5f0073fc5773cc54c04eba822af Mon Sep 17 00:00:00 2001 From: Oleksii Novikov Date: Thu, 23 Apr 2026 15:22:57 +0300 Subject: [PATCH 1/2] FINERACT-2455: Add entity-linked error when deleting WC-linked delinquency bucket/breach --- .../fineract/client/feign/FeignException.java | 23 ++++++++---- .../client/feign/FineractErrorDecoder.java | 3 +- .../util/CallFailedRuntimeException.java | 2 + .../test/helper/ErrorMessageHelper.java | 21 +++++------ .../WorkingCapitalBreachConfigStepDef.java | 14 +++++++ ...orkingCapitalDelinquencyConfigStepDef.java | 19 ++++++++-- .../stepdef/loan/WorkingCapitalStepDef.java | 27 ++++++++++++++ .../WorkingCapitalBreachConfiguration.feature | 7 ++++ ...ingCapitalDelinquencyConfiguration.feature | 7 ++++ .../DelinquencyWritePlatformServiceImpl.java | 8 ++++ .../spi/DelinquencyBucketUsageChecker.java | 26 +++++++++++++ .../starter/DelinquencyConfiguration.java | 7 +++- ...tePlatformServiceRangeChangeEventTest.java | 2 +- ...CapitalBreachWritePlatformServiceImpl.java | 21 +++++++---- .../WorkingCapitalLoanProductRepository.java | 6 +++ ...nProductDelinquencyBucketUsageChecker.java | 37 +++++++++++++++++++ 16 files changed, 195 insertions(+), 35 deletions(-) create mode 100644 fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/spi/DelinquencyBucketUsageChecker.java create mode 100644 fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/service/WorkingCapitalLoanProductDelinquencyBucketUsageChecker.java diff --git a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FeignException.java b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FeignException.java index a80430c00b2..b64cdd4e2cf 100644 --- a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FeignException.java +++ b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FeignException.java @@ -19,20 +19,27 @@ package org.apache.fineract.client.feign; import feign.Request; +import java.io.Serial; import java.nio.charset.Charset; +import lombok.Getter; /** * Base exception class for Feign client exceptions. */ public class FeignException extends RuntimeException { + @Serial private static final long serialVersionUID = 1L; private final int status; private final Request request; private final byte[] responseBody; + @Getter private final String developerMessage; + @Getter private final String userMessage; + @Getter + private final String userMessageGlobalisationCode; protected FeignException(int status, String message, Request request) { this(status, message, request, (byte[]) null); @@ -49,6 +56,7 @@ protected FeignException(int status, String message, Request request, byte[] res this.responseBody = responseBody; this.developerMessage = null; this.userMessage = null; + this.userMessageGlobalisationCode = null; } protected FeignException(int status, String message, Request request, byte[] responseBody, Throwable cause) { @@ -58,15 +66,22 @@ protected FeignException(int status, String message, Request request, byte[] res this.responseBody = responseBody; this.developerMessage = null; this.userMessage = null; + this.userMessageGlobalisationCode = null; } public FeignException(int status, String message, Request request, byte[] responseBody, String developerMessage, String userMessage) { + this(status, message, request, responseBody, developerMessage, userMessage, null); + } + + public FeignException(final int status, final String message, final Request request, final byte[] responseBody, + final String developerMessage, final String userMessage, final String userMessageGlobalisationCode) { super(message); this.status = status; this.request = request; this.responseBody = responseBody; this.developerMessage = developerMessage; this.userMessage = userMessage; + this.userMessageGlobalisationCode = userMessageGlobalisationCode; } public int status() { @@ -85,14 +100,6 @@ public String responseBodyAsString() { return responseBody != null ? new String(responseBody, Charset.defaultCharset()) : null; } - public String getDeveloperMessage() { - return developerMessage; - } - - public String getUserMessage() { - return userMessage; - } - @Override public String getMessage() { StringBuilder sb = new StringBuilder(); diff --git a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractErrorDecoder.java b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractErrorDecoder.java index 9a0867648d5..77ebd18ce58 100644 --- a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractErrorDecoder.java +++ b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/FineractErrorDecoder.java @@ -41,6 +41,7 @@ public Exception decode(String methodKey, Response response) { String developerMessage = extractField(rootNode, "developerMessage"); String userMessage = extractField(rootNode, "userMessage"); + final String userMessageGlobalisationCode = extractField(rootNode, "userMessageGlobalisationCode"); String validationErrors = extractValidationErrors(rootNode); if (developerMessage != null || userMessage != null || validationErrors != null) { @@ -49,7 +50,7 @@ public Exception decode(String methodKey, Response response) { enhancedDeveloperMessage = validationErrors; } return new FeignException(response.status(), userMessage != null ? userMessage : enhancedDeveloperMessage, - response.request(), bodyData, enhancedDeveloperMessage, userMessage); + response.request(), bodyData, enhancedDeveloperMessage, userMessage, userMessageGlobalisationCode); } } catch (IOException e) { return defaultDecoder.decode(methodKey, response); diff --git a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/util/CallFailedRuntimeException.java b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/util/CallFailedRuntimeException.java index 9e8c9ce1ceb..68a65095129 100644 --- a/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/util/CallFailedRuntimeException.java +++ b/fineract-client-feign/src/main/java/org/apache/fineract/client/feign/util/CallFailedRuntimeException.java @@ -31,11 +31,13 @@ public class CallFailedRuntimeException extends RuntimeException { private final int status; private final String developerMessage; + private final String userMessageGlobalisationCode; public CallFailedRuntimeException(FeignException cause) { super(createMessage(cause), cause); this.status = cause.status(); this.developerMessage = extractDeveloperMessage(cause); + this.userMessageGlobalisationCode = cause.getUserMessageGlobalisationCode(); } private static String createMessage(FeignException e) { diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java index fcdec85eefe..0b1b6f60900 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/helper/ErrorMessageHelper.java @@ -18,7 +18,6 @@ */ package org.apache.fineract.test.helper; -import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -29,27 +28,19 @@ import org.apache.fineract.client.models.BatchResponse; import org.apache.fineract.client.models.Header; import org.apache.fineract.client.models.LoanAccountLockResponseDTO; -import retrofit2.Response; public final class ErrorMessageHelper { public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + public static final String DATA_INTEGRITY_ISSUE_ENTITY_LINKED_CODE = "error.msg.data.integrity.issue.entity.linked"; private ErrorMessageHelper() {} - public static String requestFailed(Response response) throws IOException { - return String.format("Request failed. Error:%n%s", response.errorBody() != null ? response.errorBody().string() : null); - } - - public static String requestFailedWithCode(Response response) { - return String.format("Response has error code: %2d", response.code()); - } - public static String batchRequestFailedWithCode(BatchResponse response) { return String.format("Response has error code: %2d in request: %2d", response.getStatusCode(), response.getRequestId()); } - public static String chargeAppliesToIsInvalid(Enum chargeAppliesTo) { + public static String chargeAppliesToIsInvalid(final Enum chargeAppliesTo) { return String.format("%s is invalid input for charge applies to field", chargeAppliesTo); } @@ -1075,6 +1066,14 @@ public static String workingCapitalBreachDuplicateNameFailure(final Long id) { return String.format("Data integrity issue with resource: %d", id); } + public static String workingCapitalDelinquencyBucketLinkedToLoanProductFailure(final Long id) { + return String.format("Data integrity issue with resource: %d", id); + } + + public static String workingCapitalBreachLinkedToLoanProductFailure(final Long id) { + return String.format("Data integrity issue with resource: %d", id); + } + public static String disburseNotApprovedFailure(String status) { return String.format("Disbursement is not allowed from current status %s", status); } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java index 58a736202ec..469e50c2a8f 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java @@ -233,6 +233,20 @@ private void checkDeleteWCBreachNotFoundFailure(final Long id) { assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.workingCapitalBreachNotFoundFailure(id)); } + @Then("Admin failed to delete WC Breach that is assigned to a Working Capital Loan Product") + public void adminFailedToDeleteWCBreachAssignedToLoanProduct() { + final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + final CallFailedRuntimeException exception = fail( + () -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id)); + + assertThat(exception.getStatus()).as(ErrorMessageHelper.incorrectExpectedValueInResponse()).isEqualTo(403); + assertThat(exception.getUserMessageGlobalisationCode()).isEqualTo(ErrorMessageHelper.DATA_INTEGRITY_ISSUE_ENTITY_LINKED_CODE); + assertThat(exception.getDeveloperMessage()) // + .contains(ErrorMessageHelper.workingCapitalBreachLinkedToLoanProductFailure(id)) // + .doesNotContain("Cannot delete or update a parent row") // + .doesNotContain("m_wc_loan_product"); + } + private void checkRetrieveWCBreachNotFoundFailure(final Long id) { final CallFailedRuntimeException exception = fail( () -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id)); diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalDelinquencyConfigStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalDelinquencyConfigStepDef.java index 66c7d0c4719..4e4252f1073 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalDelinquencyConfigStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalDelinquencyConfigStepDef.java @@ -49,15 +49,12 @@ import org.apache.fineract.test.support.TestContext; import org.apache.fineract.test.support.TestContextKey; import org.assertj.core.api.SoftAssertions; -import org.springframework.beans.factory.annotation.Autowired; @Slf4j @RequiredArgsConstructor public class WorkingCapitalDelinquencyConfigStepDef extends AbstractStepDef { - @Autowired - private WorkingCapitalRequestFactory workingCapitalRequestFactory; - + private final WorkingCapitalRequestFactory workingCapitalRequestFactory; private final FineractFeignClient fineractFeignClient; @When("Admin Calls Delinquency Template") @@ -348,4 +345,18 @@ public void checkDeleteWCDelinquencyBucketDoesntExistFailure(Long id) { assertThat(exception.getDeveloperMessage()).contains(errorMessage); } + @Then("Admin failed to delete WC Delinquency Bucket that is assigned to a Working Capital Loan Product") + public void adminFailedToDeleteWCDelinquencyBucketAssignedToLoanProduct() { + final Long id = TestContext.GLOBAL.get(TestContextKey.DELINQUENCY_BUCKET_ID); + final CallFailedRuntimeException exception = fail( + () -> fineractFeignClient.delinquencyRangeAndBucketsManagement().deleteBucket(id)); + + assertThat(exception.getStatus()).as(ErrorMessageHelper.incorrectExpectedValueInResponse()).isEqualTo(403); + assertThat(exception.getUserMessageGlobalisationCode()).isEqualTo(ErrorMessageHelper.DATA_INTEGRITY_ISSUE_ENTITY_LINKED_CODE); + assertThat(exception.getDeveloperMessage()) // + .contains(ErrorMessageHelper.workingCapitalDelinquencyBucketLinkedToLoanProductFailure(id)) // + .doesNotContain("Cannot delete or update a parent row") // + .doesNotContain("m_wc_loan_product"); + } + } diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java index 47567cbdf85..b9525d5239f 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java @@ -76,6 +76,7 @@ import org.apache.fineract.test.helper.WorkingCapitalLoanProductAdvancedAccountingTestHelper; import org.apache.fineract.test.helper.WorkingCapitalLoanProductAdvancedAccountingTestHelper.AdvancedAccountingExpectation; import org.apache.fineract.test.stepdef.AbstractStepDef; +import org.apache.fineract.test.support.TestContext; import org.apache.fineract.test.support.TestContextKey; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Assertions; @@ -410,6 +411,32 @@ public void createWorkingCapitalLoanProductWithBreachAndNearBreachConfig(final D checkWorkingCapitalLoanProductCreate(); } + @When("Admin creates a new Working Capital Loan Product with existing WC Delinquency Bucket") + public void createWorkingCapitalLoanProductWithExistingDelinquencyBucket() { + final Long bucketId = TestContext.GLOBAL.get(TestContextKey.DELINQUENCY_BUCKET_ID); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // + .name(name) // + .delinquencyBucketId(bucketId); + final PostWorkingCapitalLoanProductsResponse response = createWorkingCapitalLoanProduct(request); + testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE, response); + testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_CREATE_REQUEST, request); + checkWorkingCapitalLoanProductCreate(); + } + + @When("Admin creates a new Working Capital Loan Product with existing WC Breach") + public void createWorkingCapitalLoanProductWithExistingBreach() { + final Long breachId = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); + final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // + .name(name) // + .breachId(breachId); + final PostWorkingCapitalLoanProductsResponse response = createWorkingCapitalLoanProduct(request); + testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_CREATE_RESPONSE, response); + testContext().set(TestContextKey.WORKING_CAPITAL_LOAN_PRODUCT_CREATE_REQUEST, request); + checkWorkingCapitalLoanProductCreate(); + } + @When("Admin creates a new Working Capital Loan Product with external-id") public void createWorkingCapitalLoanProductWithExternalId() { final String workingCapitalProductDefaultName = DefaultWorkingCapitalLoanProduct.WCLP.getName() diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature index 8c5ae56c8c7..60fe5c1f87c 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature @@ -78,3 +78,10 @@ Feature: Working Capital Breach Configuration | wcb_field_name_incorrect_value | | 565465 | | 0 | + + Scenario: Verify deleting WC Breach assigned to a Working Capital Loan Product is rejected with entity-linked error + When Admin creates WC Breach With Values + When Admin creates a new Working Capital Loan Product with existing WC Breach + Then Admin failed to delete WC Breach that is assigned to a Working Capital Loan Product + Then Admin deletes a Working Capital Loan Product + When Admin deletes WC Breach With Values diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature index 970b8d48359..540e3e3886d 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature @@ -70,3 +70,10 @@ Feature: Working Capital Delinquency Configuration | wcp_field_name_incorrect_value | | 565465 | | 0 | + + Scenario: Verify deleting WC Delinquency Bucket assigned to a Working Capital Loan Product is rejected with entity-linked error + When Admin creates WC Delinquency Bucket With Values + When Admin creates a new Working Capital Loan Product with existing WC Delinquency Bucket + Then Admin failed to delete WC Delinquency Bucket that is assigned to a Working Capital Loan Product + Then Admin deletes a Working Capital Loan Product + When Admin deletes WC Delinquency Bucket With Values diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java index 8b6c727a63d..63d9d80b694 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java @@ -58,6 +58,7 @@ import org.apache.fineract.portfolio.delinquency.exception.DelinquencyBucketNotFoundException; import org.apache.fineract.portfolio.delinquency.exception.DelinquencyRangeInvalidAgesException; import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePauseHelper; +import org.apache.fineract.portfolio.delinquency.spi.DelinquencyBucketUsageChecker; import org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator; import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator; import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator; @@ -91,6 +92,7 @@ public class DelinquencyWritePlatformServiceImpl implements DelinquencyWritePlat private final BusinessEventNotifierService businessEventNotifierService; private final DelinquencyWritePlatformServiceHelper delinquencyHelper; private final DelinquencyMinimumPaymentPeriodAndRuleRepository delinquencyMinimumPaymentPeriodAndRuleRepository; + private final List delinquencyBucketUsageCheckers; @Override public CommandProcessingResult createDelinquencyRange(JsonCommand command) { @@ -173,6 +175,12 @@ public CommandProcessingResult deleteDelinquencyBucket(Long delinquencyBucketId, throw new PlatformDataIntegrityException("error.msg.data.integrity.issue.entity.linked", "Data integrity issue with resource: " + delinquencyBucket.getId()); } + for (final DelinquencyBucketUsageChecker checker : delinquencyBucketUsageCheckers) { + if (checker.hasUsages(delinquencyBucket)) { + throw new PlatformDataIntegrityException("error.msg.data.integrity.issue.entity.linked", + String.format("Data integrity issue with resource: %d", delinquencyBucket.getId())); + } + } delinquencyMinimumPaymentPeriodAndRuleRepository.findByBucketId(delinquencyBucket.getId()) .ifPresent(delinquencyMinimumPaymentPeriodAndRuleRepository::delete); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/spi/DelinquencyBucketUsageChecker.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/spi/DelinquencyBucketUsageChecker.java new file mode 100644 index 00000000000..27669d0b715 --- /dev/null +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/spi/DelinquencyBucketUsageChecker.java @@ -0,0 +1,26 @@ +/** + * 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.portfolio.delinquency.spi; + +import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket; + +public interface DelinquencyBucketUsageChecker { + + boolean hasUsages(DelinquencyBucket delinquencyBucket); +} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java index 475fb062bc7..ee5f725b623 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/starter/DelinquencyConfiguration.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.portfolio.delinquency.starter; +import java.util.List; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucketMappingsRepository; @@ -39,6 +40,7 @@ import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainService; import org.apache.fineract.portfolio.delinquency.service.LoanDelinquencyDomainServiceImpl; import org.apache.fineract.portfolio.delinquency.service.PossibleNextRepaymentCalculationServiceDiscovery; +import org.apache.fineract.portfolio.delinquency.spi.DelinquencyBucketUsageChecker; import org.apache.fineract.portfolio.delinquency.validator.DelinquencyActionParseAndValidator; import org.apache.fineract.portfolio.delinquency.validator.DelinquencyBucketParseAndValidator; import org.apache.fineract.portfolio.delinquency.validator.DelinquencyRangeParseAndValidator; @@ -86,12 +88,13 @@ public DelinquencyWritePlatformService delinquencyWritePlatformService(Delinquen DelinquencyActionParseAndValidator delinquencyActionParseAndValidator, DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper, DelinquencyWritePlatformServiceHelper delinquencyWritePlatformServiceHelper, - DelinquencyMinimumPaymentPeriodAndRuleRepository delinquencyMinimumPaymentPeriodAndRuleRepository) { + DelinquencyMinimumPaymentPeriodAndRuleRepository delinquencyMinimumPaymentPeriodAndRuleRepository, + final List delinquencyBucketUsageCheckers) { return new DelinquencyWritePlatformServiceImpl(dataValidatorBucket, dataValidatorRange, repositoryRange, repositoryBucket, repositoryBucketMappings, loanDelinquencyTagRepository, loanRepository, loanProductRepository, loanDelinquencyDomainService, loanInstallmentDelinquencyTagRepository, delinquencyReadPlatformService, loanDelinquencyActionRepository, delinquencyActionParseAndValidator, delinquencyEffectivePauseHelper, businessEventNotifierService, - delinquencyWritePlatformServiceHelper, delinquencyMinimumPaymentPeriodAndRuleRepository); + delinquencyWritePlatformServiceHelper, delinquencyMinimumPaymentPeriodAndRuleRepository, delinquencyBucketUsageCheckers); } @Bean diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java index e74b9e70fd4..68d60ae054e 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java @@ -156,7 +156,7 @@ public void setUp() { repositoryBucketMappings, loanDelinquencyTagRepository, loanRepository, loanProductRepository, loanDelinquencyDomainService, loanInstallmentDelinquencyTagRepository, delinquencyReadPlatformService, loanDelinquencyActionRepository, delinquencyActionParseAndValidator, delinquencyEffectivePauseHelper, businessEventNotifierService, - delinquencyWritePlatformServiceHelper, delinquencyMinimumPaymentPeriodAndRuleRepository); + delinquencyWritePlatformServiceHelper, delinquencyMinimumPaymentPeriodAndRuleRepository, List.of()); } @AfterAll diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanbreach/service/WorkingCapitalBreachWritePlatformServiceImpl.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanbreach/service/WorkingCapitalBreachWritePlatformServiceImpl.java index d0a9b26de56..930c7a7d0ef 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanbreach/service/WorkingCapitalBreachWritePlatformServiceImpl.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanbreach/service/WorkingCapitalBreachWritePlatformServiceImpl.java @@ -35,6 +35,7 @@ import org.apache.fineract.portfolio.workingcapitalloanbreach.repository.WorkingCapitalBreachRepository; import org.apache.fineract.portfolio.workingcapitalloanbreach.validator.WorkingCapitalBreachParseAndValidator; import org.apache.fineract.portfolio.workingcapitalloanproduct.domain.WorkingCapitalBreachAmountCalculationType; +import org.apache.fineract.portfolio.workingcapitalloanproduct.repository.WorkingCapitalLoanProductRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -42,8 +43,9 @@ @RequiredArgsConstructor public class WorkingCapitalBreachWritePlatformServiceImpl implements WorkingCapitalBreachWritePlatformService { - private final WorkingCapitalBreachRepository repository; + private final WorkingCapitalBreachRepository workingCapitalBreachRepository; private final WorkingCapitalBreachParseAndValidator dataValidator; + private final WorkingCapitalLoanProductRepository workingCapitalLoanProductRepository; private static final String BREACH_FREQUENCY_PARAM = "breachFrequency"; private static final String BREACH_FREQUENCY_TYPE_PARAM = "breachFrequencyType"; @@ -63,7 +65,7 @@ public CommandProcessingResult create(final JsonCommand command) { @Override @Transactional public CommandProcessingResult update(final Long breachId, final JsonCommand command) { - final WorkingCapitalBreach existing = repository.findById(breachId) + final WorkingCapitalBreach existing = workingCapitalBreachRepository.findById(breachId) .orElseThrow(() -> new WorkingCapitalBreachNotFoundException(breachId)); final WorkingCapitalBreachRequest data = dataValidator.validateAndParse(command); @@ -77,10 +79,13 @@ public CommandProcessingResult update(final Long breachId, final JsonCommand com @Transactional public CommandProcessingResult delete(final JsonCommand command) { final Long breachId = command.entityId(); - if (!repository.existsById(breachId)) { - throw new WorkingCapitalBreachNotFoundException(breachId); + final WorkingCapitalBreach breach = workingCapitalBreachRepository.findById(breachId) + .orElseThrow(() -> new WorkingCapitalBreachNotFoundException(breachId)); + if (workingCapitalLoanProductRepository.existsByBreach(breach)) { + throw new PlatformDataIntegrityException("error.msg.data.integrity.issue.entity.linked", + String.format("Data integrity issue with resource: %d", breachId)); } - repository.deleteById(breachId); + workingCapitalBreachRepository.delete(breach); return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(breachId).build(); } @@ -93,7 +98,7 @@ private WorkingCapitalBreach createAndPersistBreach(final String name, final Int breach.setBreachFrequencyType(breachFrequencyType); breach.setBreachAmountCalculationType(breachAmountCalculationType); breach.setBreachAmount(breachAmount); - return repository.saveAndFlush(breach); + return workingCapitalBreachRepository.saveAndFlush(breach); } private WorkingCapitalBreach createAndPersistBreach(final WorkingCapitalBreachRequest request, final Map changes) { @@ -152,11 +157,11 @@ private WorkingCapitalBreach updateAndPersistBreach(final WorkingCapitalBreach i changes.put(BREACH_AMOUNT_PARAM, breachAmount); } - return changes.isEmpty() ? item : repository.save(item); + return changes.isEmpty() ? item : workingCapitalBreachRepository.save(item); } private void validateDuplicateName(final String name, final Long currentId) { - repository.findByName(name).ifPresent(existing -> { + workingCapitalBreachRepository.findByName(name).ifPresent(existing -> { final boolean sameEntity = currentId != null && Objects.equals(existing.getId(), currentId); if (!sameEntity) { throw new PlatformDataIntegrityException("error.msg.data.integrity.issue.entity.duplicated", diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/repository/WorkingCapitalLoanProductRepository.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/repository/WorkingCapitalLoanProductRepository.java index 8bbcf938aae..79ca719650f 100644 --- a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/repository/WorkingCapitalLoanProductRepository.java +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/repository/WorkingCapitalLoanProductRepository.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Optional; import org.apache.fineract.infrastructure.core.domain.ExternalId; +import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket; +import org.apache.fineract.portfolio.workingcapitalloanbreach.domain.WorkingCapitalBreach; import org.apache.fineract.portfolio.workingcapitalloanproduct.domain.WorkingCapitalLoanProduct; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; @@ -72,4 +74,8 @@ public interface WorkingCapitalLoanProductRepository @Query("select wclp FROM WorkingCapitalLoanProduct wclp where wclp.closeDate is null or wclp.closeDate >= :businessDate") List fetchActiveWorkingCapitalLoanProducts(LocalDate businessDate); + + boolean existsByDelinquencyBucket(DelinquencyBucket delinquencyBucket); + + boolean existsByBreach(WorkingCapitalBreach breach); } diff --git a/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/service/WorkingCapitalLoanProductDelinquencyBucketUsageChecker.java b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/service/WorkingCapitalLoanProductDelinquencyBucketUsageChecker.java new file mode 100644 index 00000000000..526015bb1c2 --- /dev/null +++ b/fineract-working-capital-loan/src/main/java/org/apache/fineract/portfolio/workingcapitalloanproduct/service/WorkingCapitalLoanProductDelinquencyBucketUsageChecker.java @@ -0,0 +1,37 @@ +/** + * 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.portfolio.workingcapitalloanproduct.service; + +import lombok.RequiredArgsConstructor; +import org.apache.fineract.portfolio.delinquency.domain.DelinquencyBucket; +import org.apache.fineract.portfolio.delinquency.spi.DelinquencyBucketUsageChecker; +import org.apache.fineract.portfolio.workingcapitalloanproduct.repository.WorkingCapitalLoanProductRepository; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class WorkingCapitalLoanProductDelinquencyBucketUsageChecker implements DelinquencyBucketUsageChecker { + + private final WorkingCapitalLoanProductRepository repository; + + @Override + public boolean hasUsages(final DelinquencyBucket delinquencyBucket) { + return repository.existsByDelinquencyBucket(delinquencyBucket); + } +} From 6d1477b7b578c12d8c56c762aad9f2ba9e063251 Mon Sep 17 00:00:00 2001 From: MarianaDmytrivBinariks Date: Thu, 30 Apr 2026 17:24:37 +0300 Subject: [PATCH 2/2] FINERACT-2455: e2e test scenarios for delete breach and delinquency bucket assigned to loan entity error handling --- .../WorkingCapitalBreachConfigStepDef.java | 37 +++++++++++++------ .../WorkingCapitalLoanAccountStepDef.java | 9 +++++ ...WorkingCapitalNearBreachConfigStepDef.java | 33 +---------------- .../stepdef/loan/WorkingCapitalStepDef.java | 2 +- .../WorkingCapitalBreachConfiguration.feature | 10 ++++- ...ingCapitalDelinquencyConfiguration.feature | 1 + ...kingCapitalNearBreachConfiguration.feature | 4 +- 7 files changed, 48 insertions(+), 48 deletions(-) diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java index 469e50c2a8f..91fb857e9a4 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalBreachConfigStepDef.java @@ -108,17 +108,6 @@ public void checkCreatedBreachHasTheFollowingValues() { checkBreachData(request, data); } - /* - * @Then("Get Breach has the following values") public void getBreachHasTheFollowingValues() { final Long id = - * TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); final WorkingCapitalBreachData data = ok(() - * -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id)); // final - * WorkingCapitalBreachTemplateResponse template = ok( // () -> - * fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreachTemplate()); - * assertThat(data).isNotNull(); /// assertThat(template).isNotNull(); // - * assertThat(template.getBreachFrequencyTypeOptions()).isNotNull().isNotEmpty(); - * //assertThat(template.getBreachAmountCalculationTypeOptions()).isNotNull().isNotEmpty(); } - */ - @When("Admin modifies WC Breach With Values") public void adminModifiesWCBreachWithValues() { final Long id = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); @@ -233,9 +222,22 @@ private void checkDeleteWCBreachNotFoundFailure(final Long id) { assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper.workingCapitalBreachNotFoundFailure(id)); } + public void deleteWCBreachAssignedToLoanEntityFailure(Long id, String errorCode, String errorMessage) { + // final Long id = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + final CallFailedRuntimeException exception = fail( + () -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id)); + + assertThat(exception.getStatus()).as(ErrorMessageHelper.incorrectExpectedValueInResponse()).isEqualTo(403); + assertThat(exception.getUserMessageGlobalisationCode()).isEqualTo(errorCode); + assertThat(exception.getDeveloperMessage()) // + .contains(errorMessage) // + .doesNotContain("Cannot delete or update a parent row") // + .doesNotContain("m_wc_loan_product"); + } + @Then("Admin failed to delete WC Breach that is assigned to a Working Capital Loan Product") public void adminFailedToDeleteWCBreachAssignedToLoanProduct() { - final Long id = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + final Long id = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); final CallFailedRuntimeException exception = fail( () -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id)); @@ -247,6 +249,17 @@ public void adminFailedToDeleteWCBreachAssignedToLoanProduct() { .doesNotContain("m_wc_loan_product"); } + @Then("Admin failed to delete WC Breach that is assigned to a Working Capital Loan Account") + public void adminFailedToDeleteWCBreachAssignedToLoanAccount() { + final Long id = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + String errorMessage = "The request caused a data integrity issue to be fired by the database."; + final CallFailedRuntimeException exception = fail( + () -> fineractFeignClient.workingCapitalBreaches().deleteWorkingCapitalBreach(id)); + assertThat(exception.getStatus()).isEqualTo(403); + assertThat(exception.getMessage()).contains(errorMessage); + + } + private void checkRetrieveWCBreachNotFoundFailure(final Long id) { final CallFailedRuntimeException exception = fail( () -> fineractFeignClient.workingCapitalBreaches().retrieveWorkingCapitalBreach(id)); diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java index 41c90e7a7bd..9786ea777d2 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalLoanAccountStepDef.java @@ -1559,6 +1559,15 @@ public void createLoanWithBreachNearBreachFromWCLPOverrideAllowedData(DataTable createWorkingCapitalLoanAccountWithBreachNearBreachData(loanData, breachIdFromWCLP, nearBreachIdFromWCLP); } + @Then("Admin creates working capital loan with with breach on {string} date") + public void createLoanWithBreachOverrideAllowedWithBreachData(String submittedOnDate) { + final Long breachId = createBreachAndGetId(); + + final PostWorkingCapitalLoansRequest loansRequest = createWorkingCapitalLoanAccountDefaultRequest(submittedOnDate) + .breachId(breachId); + createWorkingCapitalLoanAccount(loansRequest); + } + @Then("Admin creates working capital loan with with breach and near breach on {string} date") public void createLoanWithBreachOverrideAllowedWithBreachAndNearBreachData(String submittedOnDate) { final Long breachId = createBreachAndGetId(); diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalNearBreachConfigStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalNearBreachConfigStepDef.java index 18957250129..b3201f78d10 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalNearBreachConfigStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalNearBreachConfigStepDef.java @@ -191,27 +191,13 @@ public void adminRetrieveWCNearBreachWithIncorrectIdFailure(final Integer id) { checkRetrieveWCNearBreachNotFoundFailure(Long.valueOf(id)); } - @Then("Admin failed to delete WC Near Breach that is still assigned to WC loan product") + @Then("Admin failed to delete WC Near Breach that is still assigned to WC loan product or account") public void adminDeleteWCBreachAssignedToWCLPFailure() { final Long id = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID); String errorMessage = "The request caused a data integrity issue to be fired by the database."; - // checkDeleteWCNearBreachFailure(id, 403, errorMessage); final CallFailedRuntimeException exception = fail( () -> fineractFeignClient.workingCapitalNearBreaches().deleteWorkingCapitalNearBreach(id)); assertThat(exception.getStatus()).isEqualTo(403); - // assertThat(exception.getDeveloperMessage()).contains(errorMessage); - assertThat(exception.getMessage()).contains(errorMessage); - } - - @Then("Admin failed to delete WC Near Breach that is still assigned to WC loan account") - public void adminDeleteWCBreachAssignedToWCLAccountFailure() { - final Long id = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_NEAR_BREACH_ID); - String errorMessage = "The request caused a data integrity issue to be fired by the database."; - // checkDeleteWCNearBreachFailure(id, 403, errorMessage); - final CallFailedRuntimeException exception = fail( - () -> fineractFeignClient.workingCapitalNearBreaches().deleteWorkingCapitalNearBreach(id)); - assertThat(exception.getStatus()).isEqualTo(403); - // assertThat(exception.getDeveloperMessage()).contains(errorMessage); assertThat(exception.getMessage()).contains(errorMessage); } @@ -270,13 +256,6 @@ private void checkUpdateWCNearBreachWithInvalidDataFailure(final Long id, final } private void checkDeleteWCNearBreachNotFoundFailure(final Long id) { - /* - * final CallFailedRuntimeException exception = fail( () -> - * fineractFeignClient.workingCapitalNearBreaches().deleteWorkingCapitalNearBreach(id)); - * assertThat(exception.getStatus()).isEqualTo(404); - * assertThat(exception.getDeveloperMessage()).contains(ErrorMessageHelper. - * workingCapitalNearBreachNotFoundFailure(id)); - */ String errorMessage = ErrorMessageHelper.workingCapitalNearBreachNotFoundFailure(id); checkDeleteWCNearBreachFailure(id, 404, errorMessage); } @@ -286,18 +265,8 @@ private void checkDeleteWCNearBreachFailure(final Long id, int statusCode, Strin () -> fineractFeignClient.workingCapitalNearBreaches().deleteWorkingCapitalNearBreach(id)); assertThat(exception.getStatus()).isEqualTo(statusCode); assertThat(exception.getDeveloperMessage()).contains(errorMessage); - // assertThat(exception.getMessage()).contains(errorMessage); } - /* - * private void checkDeleteWCNearBreachFailure(final Long id, int statusCode, String errorMessage) { final - * CallFailedRuntimeException exception = fail( () -> - * fineractFeignClient.workingCapitalNearBreaches().deleteWorkingCapitalNearBreach(id)); - * assertThat(exception.getStatus()).isEqualTo(statusCode); // - * assertThat(exception.getDeveloperMessage()).contains(errorMessage); - * assertThat(exception.getMessage()).contains(errorMessage); } - */ - private void checkRetrieveWCNearBreachNotFoundFailure(final Long id) { final CallFailedRuntimeException exception = fail( () -> fineractFeignClient.workingCapitalNearBreaches().retrieveWorkingCapitalNearBreach(id)); diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java index b9525d5239f..460f462f730 100644 --- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java +++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java @@ -426,7 +426,7 @@ public void createWorkingCapitalLoanProductWithExistingDelinquencyBucket() { @When("Admin creates a new Working Capital Loan Product with existing WC Breach") public void createWorkingCapitalLoanProductWithExistingBreach() { - final Long breachId = TestContext.GLOBAL.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); + final Long breachId = TestContext.INSTANCE.get(TestContextKey.WORKING_CAPITAL_BREACH_ID); final String name = DefaultWorkingCapitalLoanProduct.WCLP.getName() + Utils.randomStringGenerator("_", 10); final PostWorkingCapitalLoanProductsRequest request = workingCapitalRequestFactory.defaultWorkingCapitalLoanProductRequest() // .name(name) // diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature index 60fe5c1f87c..c93637a8c1b 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalBreachConfiguration.feature @@ -79,9 +79,17 @@ Feature: Working Capital Breach Configuration | 565465 | | 0 | - Scenario: Verify deleting WC Breach assigned to a Working Capital Loan Product is rejected with entity-linked error + @TestRailId:C78842 + Scenario: Verify deleting WC Breach assigned to a Working Capital Loan Product is rejected with entity-linked error - UC9.1 When Admin creates WC Breach With Values When Admin creates a new Working Capital Loan Product with existing WC Breach Then Admin failed to delete WC Breach that is assigned to a Working Capital Loan Product Then Admin deletes a Working Capital Loan Product When Admin deletes WC Breach With Values + + @TestRailId:C78843 + Scenario: Verify deleting Working Capital Breach Configuration that is still assigned to WC loan account failure - UC9.2 + When Admin sets the business date to "01 January 2027" + And Admin creates a client with random data + And Admin creates working capital loan with with breach on "01 January 2027" date + Then Admin failed to delete WC Breach that is assigned to a Working Capital Loan Account diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature index 540e3e3886d..b7554416bf0 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalDelinquencyConfiguration.feature @@ -71,6 +71,7 @@ Feature: Working Capital Delinquency Configuration | 565465 | | 0 | + @TestRailId:C78841 Scenario: Verify deleting WC Delinquency Bucket assigned to a Working Capital Loan Product is rejected with entity-linked error When Admin creates WC Delinquency Bucket With Values When Admin creates a new Working Capital Loan Product with existing WC Delinquency Bucket diff --git a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalNearBreachConfiguration.feature b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalNearBreachConfiguration.feature index ea74d99cbbc..5426ea9a4be 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalNearBreachConfiguration.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalNearBreachConfiguration.feature @@ -84,7 +84,7 @@ Feature: Working Capital Near Breach Configuration @TestRailId:C76705 Scenario: Verify deleting Working Capital Near Breach Configuration that is still assigned to WCLP failure - UC9.1 When Admin creates a new Working Capital Loan Product with breach and near breach - Then Admin failed to delete WC Near Breach that is still assigned to WC loan product + Then Admin failed to delete WC Near Breach that is still assigned to WC loan product or account Then Admin deletes a Working Capital Loan Product Then Admin deletes WC Breach With Values Then Admin deletes WC Near Breach With Values @@ -94,7 +94,7 @@ Feature: Working Capital Near Breach Configuration When Admin sets the business date to "01 January 2027" And Admin creates a client with random data And Admin creates working capital loan with with breach and near breach on "01 January 2027" date - Then Admin failed to delete WC Near Breach that is still assigned to WC loan product + Then Admin failed to delete WC Near Breach that is still assigned to WC loan product or account