FINERACT-2058: loan can be marked as fraud in any status
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 5c97594..4793703 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -2550,11 +2550,6 @@
 
         Loan loan = this.loanAssembler.assembleFrom(loanId);
         final Map<String, Object> changes = new LinkedHashMap<>();
-
-        if (loan.isApproved() || loan.isSubmittedAndPendingApproval()) {
-            throw new GeneralPlatformDomainRuleException("error.msg.loan.mark.as.fraud.not.allowed",
-                    "Loan Id: " + loanId + " mark as fraud is not allowed as loan status is not active", loan.getStatus().getCode());
-        }
         final boolean fraud = command.booleanPrimitiveValueOfParameterNamed(LoanApiConstants.FRAUD_ATTRIBUTE_NAME);
         if (loan.isFraud() != fraud) {
             loan.markAsFraud(fraud);
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 f0056b2..0d74fc1 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
@@ -401,6 +401,10 @@
                 .transactionAmount(amount).locale("en"));
     }
 
+    protected void undoDisbursement(Integer loanId) {
+        loanTransactionHelper.undoDisbursal(loanId);
+    }
+
     protected void verifyJournalEntries(Long loanId, Journal... entries) {
         GetJournalEntriesTransactionIdResponse journalEntriesForLoan = journalEntryHelper.getJournalEntriesForLoan(loanId);
         Assertions.assertEquals(entries.length, journalEntriesForLoan.getPageItems().size());
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java
index 771dafe..1cca12c 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountFraudTest.java
@@ -21,63 +21,50 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.builder.ResponseSpecBuilder;
-import io.restassured.http.ContentType;
-import io.restassured.specification.RequestSpecification;
-import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
 import java.time.LocalDate;
-import java.util.HashMap;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
 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.PostLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansResponse;
 import org.apache.fineract.client.models.PutLoansLoanIdResponse;
-import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
-import org.apache.fineract.integrationtests.common.BusinessDateHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
-import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
 import org.apache.fineract.integrationtests.common.Utils;
-import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
-import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
 import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension;
-import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 @Slf4j
 @ExtendWith(LoanTestLifecycleExtension.class)
-public class LoanAccountFraudTest {
+public class LoanAccountFraudTest extends BaseLoanIntegrationTest {
 
-    private ResponseSpecification responseSpec;
-    private ResponseSpecification responseSpecError;
-    private RequestSpecification requestSpec;
-    private LoanTransactionHelper loanTransactionHelper;
-    private final String amountVal = "1000";
+    private static final double AMOUNT = 100.0;
+    private static final String COMMAND = "markAsFraud";
     private LocalDate todaysDate;
     private String operationDate;
 
     @BeforeEach
     public void setup() {
         Utils.initializeRESTAssured();
-        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
-        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
-        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
-        this.responseSpecError = new ResponseSpecBuilder().expectStatusCode(403).build();
-        this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
-
         this.todaysDate = Utils.getLocalDateOfTenant();
         this.operationDate = Utils.dateFormatter.format(this.todaysDate);
     }
 
     @Test
     public void testMarkLoanAsFraud() {
-        try {
-            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
-            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate);
-            final String command = "markAsFraud";
-            // Client and Loan account creation
-            final Integer loanId = createAccounts(15, 1);
+        runAt(operationDate, () -> {
+
+            final Long clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            PostLoanProductsRequest loanProductsRequest = createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct();
+            PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(loanProductsRequest);
+
+            PostLoansResponse postLoansResponse = loanTransactionHelper
+                    .applyLoan(applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, AMOUNT, 1));
+            Integer loanId = postLoansResponse.getLoanId().intValue();
 
             GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
             assertNotNull(getLoansLoanIdResponse);
@@ -87,16 +74,46 @@
             assertEquals(Boolean.FALSE, getLoansLoanIdResponse.getFraud());
 
             String payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "true");
-            // Send the request, not expecting any errors (because only open loan restriction removed)
-            PutLoansLoanIdResponse putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload,
-                    this.responseSpecError);
+            PutLoansLoanIdResponse putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, responseSpec);
+            assertNotNull(putLoansLoanIdResponse);
 
+            getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            assertNotNull(getLoansLoanIdResponse);
+            assertNotNull(getLoansLoanIdResponse.getFraud());
+            assertEquals(Boolean.TRUE, getLoansLoanIdResponse.getFraud());
             String statusCode = getLoansLoanIdResponse.getStatus().getCode();
             log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
 
+            payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "false");
+            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, responseSpec);
+            assertNotNull(putLoansLoanIdResponse);
+
+            getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            assertNotNull(getLoansLoanIdResponse);
+            assertNotNull(getLoansLoanIdResponse.getFraud());
+            assertEquals(Boolean.FALSE, getLoansLoanIdResponse.getFraud());
+            statusCode = getLoansLoanIdResponse.getStatus().getCode();
+            log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
+
             // Approve the Loan active
-            loanTransactionHelper.approveLoan(operationDate, this.amountVal, loanId, null);
-            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload, this.responseSpecError);
+            PostLoansLoanIdResponse approvedLoanResult = loanTransactionHelper.approveLoan(postLoansResponse.getResourceId(),
+                    approveLoanRequest(AMOUNT, operationDate));
+            assertNotNull(approvedLoanResult);
+
+            payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "true");
+            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, responseSpec);
+            assertNotNull(putLoansLoanIdResponse);
+
+            getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            assertNotNull(getLoansLoanIdResponse);
+            assertNotNull(getLoansLoanIdResponse.getFraud());
+            assertEquals(Boolean.TRUE, getLoansLoanIdResponse.getFraud());
+            statusCode = getLoansLoanIdResponse.getStatus().getCode();
+            log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
+
+            payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "false");
+            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, responseSpec);
+            assertNotNull(putLoansLoanIdResponse);
 
             // Default values Not Null and False
             getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
@@ -106,10 +123,11 @@
             statusCode = getLoansLoanIdResponse.getStatus().getCode();
             log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
 
-            loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, this.amountVal);
+            disburseLoan(loanId.longValue(), BigDecimal.valueOf(AMOUNT), operationDate);
 
             // Mark On the Fraud
-            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload, this.responseSpec);
+            payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "true");
+            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, responseSpec);
             assertNotNull(putLoansLoanIdResponse);
 
             getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
@@ -119,49 +137,33 @@
 
             // Mark Off the Fraud
             payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "false");
-            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, command, payload, this.responseSpec);
+            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, this.responseSpec);
             assertNotNull(putLoansLoanIdResponse);
 
             getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
             assertNotNull(getLoansLoanIdResponse);
             assertNotNull(getLoansLoanIdResponse.getFraud());
             assertEquals(Boolean.FALSE, getLoansLoanIdResponse.getFraud());
-        } finally {
-            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
-        }
+
+            payload = loanTransactionHelper.getLoanFraudPayloadAsJSON("fraud", "true");
+            putLoansLoanIdResponse = loanTransactionHelper.modifyLoanCommand(loanId, COMMAND, payload, responseSpec);
+            assertNotNull(putLoansLoanIdResponse);
+
+            getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            assertNotNull(getLoansLoanIdResponse);
+            assertNotNull(getLoansLoanIdResponse.getFraud());
+            assertEquals(Boolean.TRUE, getLoansLoanIdResponse.getFraud());
+            statusCode = getLoansLoanIdResponse.getStatus().getCode();
+            log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
+
+            undoDisbursement(loanId);
+
+            getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            assertNotNull(getLoansLoanIdResponse);
+            assertNotNull(getLoansLoanIdResponse.getFraud());
+            assertEquals(Boolean.TRUE, getLoansLoanIdResponse.getFraud());
+            statusCode = getLoansLoanIdResponse.getStatus().getCode();
+            log.info("Loan with Id {} is with Status {}", getLoansLoanIdResponse.getId(), statusCode);
+        });
     }
-
-    private Integer createAccounts(final Integer daysToSubtract, final Integer numberOfRepayments) {
-        // Client and Loan account creation
-        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null);
-
-        // Older date to have more than one overdue installment
-        final LocalDate transactionDate = this.todaysDate.minusDays(daysToSubtract + (30 * (numberOfRepayments - 1)));
-        String operationDate = Utils.dateFormatter.format(transactionDate);
-
-        return createLoanAccount(loanTransactionHelper, clientId.toString(), getLoanProductsProductResponse.getId().toString(),
-                operationDate, amountVal, numberOfRepayments.toString());
-    }
-
-    private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
-            final Integer delinquencyBucketId) {
-        final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);
-        final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
-        return loanTransactionHelper.getLoanProduct(loanProductId);
-    }
-
-    private Integer createLoanAccount(final LoanTransactionHelper loanTransactionHelper, final String clientId, final String loanProductId,
-            final String operationDate, final String principalAmount, final String numberOfRepayments) {
-        final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principalAmount)
-                .withLoanTermFrequency(numberOfRepayments).withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments)
-                .withRepaymentEveryAfter("1").withRepaymentFrequencyTypeAsMonths() //
-                .withInterestRatePerPeriod("0") //
-                .withExpectedDisbursementDate(operationDate) //
-                .withInterestTypeAsDecliningBalance() //
-                .withSubmittedOnDate(operationDate) //
-                .build(clientId, loanProductId, null);
-        return loanTransactionHelper.getLoanId(loanApplicationJSON);
-    }
-
 }