You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by GitBox <gi...@apache.org> on 2022/12/01 18:40:32 UTC

[GitHub] [fineract] galovics commented on a diff in pull request #2777: [FINERACT-1678] Loan COB Catch Up

galovics commented on code in PR #2777:
URL: https://github.com/apache/fineract/pull/2777#discussion_r1037387206


##########
fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java:
##########
@@ -116,6 +135,12 @@ public void execute(List<Long> loanIds, String jobName) {
         }
     }
 
+    private LocalDate getOldestCOBBusinessDate(List<Long> loanIds) {
+        List<Loan> oldestCOBProcessedLoanByLoanIds = loanRepository.findOldestCOBProcessedLoanByLoanIds(loanIds);

Review Comment:
   I think this'll be a performance issue potentially cause we're retrieving Loan objects. Can't we do the "oldest" calculation in the query?



##########
fineract-provider/src/main/java/org/apache/fineract/cob/api/LoanCOBCatchUpApiResource.java:
##########
@@ -0,0 +1,73 @@
+/**
+ * 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.cob.api;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
+import org.apache.fineract.cob.service.LoanCOBCatchUpService;
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.springframework.stereotype.Component;
+
+@Path("/loans")
+@Component
+@Tag(name = "Loan COB Catch Up", description = "")
+@RequiredArgsConstructor
+public class LoanCOBCatchUpApiResource {
+
+    private final DefaultToApiJsonSerializer<OldestCOBProcessedLoanDTO> oldestCOBProcessedLoanSerializeService;
+    private final LoanCOBCatchUpService loanCOBCatchUpService;
+
+    @GET
+    @Path("oldestCOBProcessedLoan")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Retrieves the oldest COB processed loan", description = "Retrieves the COB business date and the oldest COB processed loan")
+    @ApiResponses({
+            @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanCOBCatchUpApiResourceSwagger.GetOldestCOBProcessedLoanResponse.class))) })
+    public String getOldestCOBProcessedLoan() {
+        OldestCOBProcessedLoanDTO response = loanCOBCatchUpService.getOldestCOBProcessedLoan();
+
+        return oldestCOBProcessedLoanSerializeService.serialize(response);
+    }
+
+    @POST
+    @Path("loanCOBCatchUp")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @Operation(summary = "Executes Loan COB Catch Up", description = "Executes the Loan COB job on every day from the oldest Loan to the current COB business date")
+    @ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
+    public Response executeLoanCOBCatchUp() {
+        loanCOBCatchUpService.executeLoanCOBCatchUp();
+
+        return Response.status(200).build();

Review Comment:
   Make it HTTP 202.



##########
fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanCOBCatchUpServiceImpl.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * 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.cob.service;
+
+import com.google.gson.Gson;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
+import org.apache.fineract.cob.loan.LoanCOBConstant;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.core.domain.FineractContext;
+import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
+import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
+import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository;
+import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
+import org.apache.fineract.infrastructure.jobs.service.JobStarter;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobParametersInvalidException;
+import org.springframework.batch.core.configuration.JobLocator;
+import org.springframework.batch.core.launch.NoSuchJobException;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRestartException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService, InitializingBean {
+
+    private final LoanRepository loanRepository;
+    private final JobStarter jobStarter;
+    private final JobLocator jobLocator;
+    private final ScheduledJobDetailRepository scheduledJobDetailRepository;
+    private Gson gson;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.gson = GoogleGsonSerializerHelper.createSimpleGson();
+    }
+
+    @Override
+    public OldestCOBProcessedLoanDTO getOldestCOBProcessedLoan() {
+        List<Loan> oldestCOBProcessedLoan = loanRepository.findOldestCOBProcessedLoan();
+        OldestCOBProcessedLoanDTO oldestCOBProcessedLoanDTO = new OldestCOBProcessedLoanDTO();
+        oldestCOBProcessedLoanDTO.setLoanIds(oldestCOBProcessedLoan.stream().map(Loan::getId).toList());
+        oldestCOBProcessedLoanDTO
+                .setCobProcessedDate(oldestCOBProcessedLoan.stream().map(Loan::getLastClosedBusinessDate).findFirst().get());
+        oldestCOBProcessedLoanDTO.setCobBusinessDate(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE));
+        return oldestCOBProcessedLoanDTO;
+    }
+
+    @Override
+    public void executeLoanCOBCatchUp() {
+        FineractContext context = ThreadLocalContextUtil.getContext();
+        CompletableFuture<Void> executeCatchUpFuture = CompletableFuture.runAsync(() -> executeLoanCOBCatchUpAsync(context));
+    }
+
+    private void executeLoanCOBCatchUpAsync(FineractContext context) {
+        ThreadLocalContextUtil.init(context);
+        LocalDate oldestCOBProcessedDate = loanRepository.findOldestCOBProcessedLoan().get(0).getLastClosedBusinessDate().plusDays(1);
+        LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE);
+        try {

Review Comment:
   Shouldn't we at least check wheteher the oldest date is the same as the business date and if they're the same, just skip the execution (or throw an exception)?



##########
fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanCOBCatchUpServiceImpl.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * 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.cob.service;
+
+import com.google.gson.Gson;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
+import org.apache.fineract.cob.loan.LoanCOBConstant;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.core.domain.FineractContext;
+import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
+import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
+import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository;
+import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
+import org.apache.fineract.infrastructure.jobs.service.JobStarter;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobParametersInvalidException;
+import org.springframework.batch.core.configuration.JobLocator;
+import org.springframework.batch.core.launch.NoSuchJobException;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRestartException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService, InitializingBean {
+
+    private final LoanRepository loanRepository;
+    private final JobStarter jobStarter;
+    private final JobLocator jobLocator;
+    private final ScheduledJobDetailRepository scheduledJobDetailRepository;
+    private Gson gson;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.gson = GoogleGsonSerializerHelper.createSimpleGson();
+    }
+
+    @Override
+    public OldestCOBProcessedLoanDTO getOldestCOBProcessedLoan() {
+        List<Loan> oldestCOBProcessedLoan = loanRepository.findOldestCOBProcessedLoan();
+        OldestCOBProcessedLoanDTO oldestCOBProcessedLoanDTO = new OldestCOBProcessedLoanDTO();
+        oldestCOBProcessedLoanDTO.setLoanIds(oldestCOBProcessedLoan.stream().map(Loan::getId).toList());
+        oldestCOBProcessedLoanDTO
+                .setCobProcessedDate(oldestCOBProcessedLoan.stream().map(Loan::getLastClosedBusinessDate).findFirst().get());
+        oldestCOBProcessedLoanDTO.setCobBusinessDate(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE));
+        return oldestCOBProcessedLoanDTO;
+    }
+
+    @Override
+    public void executeLoanCOBCatchUp() {
+        FineractContext context = ThreadLocalContextUtil.getContext();
+        CompletableFuture<Void> executeCatchUpFuture = CompletableFuture.runAsync(() -> executeLoanCOBCatchUpAsync(context));

Review Comment:
   We should be very careful with this since this is using the default ForkJoinPool or a Thread per task based executor.
   Let's configure a proper AsyncTaskExecutor to submit this task (might be already configured in the app, haven't checked)



##########
fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/domain/ScheduledJobDetailRepository.java:
##########
@@ -45,4 +45,7 @@
     @Query("select jobDetail from ScheduledJobDetail jobDetail where jobDetail.nodeId = :nodeId or jobDetail.nodeId = 0")
     List<ScheduledJobDetail> findAllJobs(@Param("nodeId") Integer nodeId);
 
+    @Query("select jobDetail from ScheduledJobDetail jobDetail where jobDetail.jobName = :jobName")

Review Comment:
   No need for the custom Query. The generated Spring Data JPA one will be the same.



##########
fineract-provider/src/main/java/org/apache/fineract/cob/service/InlineLoanCOBExecutorServiceImpl.java:
##########
@@ -94,6 +99,20 @@ public CommandProcessingResult executeInlineJob(JsonCommand command, String jobN
 
     @Override
     public void execute(List<Long> loanIds, String jobName) {
+        LocalDate oldestCOBBusinessDate = getOldestCOBBusinessDate(loanIds);
+        LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE);

Review Comment:
   Aren't we missing a condition here to check whether the oldest date is the same as the current COB business date and if that's the case, just skip the execution? And probably we should do the same thing to remove loans which is in this state.
   Might be wrong though, what do you think?
   cc @adamsaghy 



##########
fineract-provider/src/main/java/org/apache/fineract/cob/service/LoanCOBCatchUpServiceImpl.java:
##########
@@ -0,0 +1,105 @@
+/**
+ * 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.cob.service;
+
+import com.google.gson.Gson;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
+import org.apache.fineract.cob.loan.LoanCOBConstant;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.core.domain.FineractContext;
+import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
+import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
+import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
+import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetailRepository;
+import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
+import org.apache.fineract.infrastructure.jobs.service.JobStarter;
+import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
+import org.springframework.batch.core.Job;
+import org.springframework.batch.core.JobParametersInvalidException;
+import org.springframework.batch.core.configuration.JobLocator;
+import org.springframework.batch.core.launch.NoSuchJobException;
+import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
+import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
+import org.springframework.batch.core.repository.JobRestartException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor
+public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService, InitializingBean {
+
+    private final LoanRepository loanRepository;
+    private final JobStarter jobStarter;
+    private final JobLocator jobLocator;
+    private final ScheduledJobDetailRepository scheduledJobDetailRepository;
+    private Gson gson;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        this.gson = GoogleGsonSerializerHelper.createSimpleGson();
+    }
+
+    @Override
+    public OldestCOBProcessedLoanDTO getOldestCOBProcessedLoan() {
+        List<Loan> oldestCOBProcessedLoan = loanRepository.findOldestCOBProcessedLoan();
+        OldestCOBProcessedLoanDTO oldestCOBProcessedLoanDTO = new OldestCOBProcessedLoanDTO();
+        oldestCOBProcessedLoanDTO.setLoanIds(oldestCOBProcessedLoan.stream().map(Loan::getId).toList());
+        oldestCOBProcessedLoanDTO
+                .setCobProcessedDate(oldestCOBProcessedLoan.stream().map(Loan::getLastClosedBusinessDate).findFirst().get());
+        oldestCOBProcessedLoanDTO.setCobBusinessDate(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE));
+        return oldestCOBProcessedLoanDTO;
+    }
+
+    @Override
+    public void executeLoanCOBCatchUp() {
+        FineractContext context = ThreadLocalContextUtil.getContext();
+        CompletableFuture<Void> executeCatchUpFuture = CompletableFuture.runAsync(() -> executeLoanCOBCatchUpAsync(context));
+    }
+
+    private void executeLoanCOBCatchUpAsync(FineractContext context) {
+        ThreadLocalContextUtil.init(context);

Review Comment:
   Since this is being executed on a separate thread, let's put it into a try-finally block to make sure we clean up the ThreadLocalContextUtil (with a reset call).



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java:
##########
@@ -0,0 +1,158 @@
+/**
+ * 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.integrationtests.inlinecob;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.path.json.JsonPath;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+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.CollateralManagementHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+@Order(1)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class InlineLoanCOBTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private InlineLoanCOBHelper inlineLoanCOBHelper;
+    private LoanTransactionHelper loanTransactionHelper;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        requestSpec.header("Fineract-Platform-TenantId", "default");
+        responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+        inlineLoanCOBHelper = new InlineLoanCOBHelper(requestSpec, responseSpec);
+    }
+
+    @Test
+    public void testInlineCOBCatchUpLoans() {

Review Comment:
   I don't think this is testing the catch-up behavior, does it?



##########
integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java:
##########
@@ -0,0 +1,158 @@
+/**
+ * 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.integrationtests.inlinecob;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.path.json.JsonPath;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+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.CollateralManagementHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanStatusChecker;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+@Order(1)
+@TestMethodOrder(MethodOrderer.MethodName.class)

Review Comment:
   Why do we need these 2 annotations?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@fineract.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org