You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2023/02/17 12:09:33 UTC

[fineract] branch develop updated: [FINERACT-1884] write API calls on loan makes Inline COB and catches up the loan to the current COB business date

This is an automated email from the ASF dual-hosted git repository.

arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 3744ad54e [FINERACT-1884] write API calls on loan makes Inline COB and catches up the loan to the current COB business date
3744ad54e is described below

commit 3744ad54eee32b78c87fecd39aab29ca765780d2
Author: taskain7 <ta...@gmail.com>
AuthorDate: Thu Feb 16 12:51:27 2023 +0100

    [FINERACT-1884] write API calls on loan makes Inline COB and catches up the loan to the current COB business date
---
 .../jobs/filter/LoanCOBApiFilter.java              | 16 +++++--
 .../jobs/filter/LoanCOBApiFilterTest.java          | 33 +++++++++++++-
 .../inlinecob/InlineLoanCOBTest.java               | 51 +++++++++++++++++++++-
 3 files changed, 94 insertions(+), 6 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java
index 9dd401fec..571fb3a25 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilter.java
@@ -31,18 +31,22 @@ import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.UriInfo;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.batch.domain.BatchRequest;
 import org.apache.fineract.batch.domain.BatchResponse;
 import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
 import org.apache.fineract.cob.service.LoanAccountLockService;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
 import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
 import org.apache.fineract.infrastructure.core.filters.BatchFilter;
 import org.apache.fineract.infrastructure.core.filters.BatchFilterChain;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException;
 import org.apache.http.HttpStatus;
 import org.springframework.http.HttpMethod;
@@ -57,6 +61,7 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
     private final LoanAccountLockService loanAccountLockService;
     private final PlatformSecurityContext context;
     private final InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService;
+    private final LoanRepository loanRepository;
 
     private static final List<HttpMethod> HTTP_METHODS = List.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE);
 
@@ -115,9 +120,9 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
                     proceed(filterChain, request, response);
                 } else {
                     try {
-                        List<Long> result = calculateRelevantLoanIds(request.getPathInfo());
-                        if (isLoanSoftLocked(result)) {
-                            executeInlineCob(result);
+                        List<Long> loanIds = calculateRelevantLoanIds(request.getPathInfo());
+                        if (isLoanSoftLocked(loanIds) || isLoanBehind(loanIds)) {
+                            executeInlineCob(loanIds);
                         }
                         proceed(filterChain, request, response);
                     } catch (LoanIdsHardLockedException e) {
@@ -130,6 +135,11 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
         }
     }
 
+    private boolean isLoanBehind(List<Long> loanIds) {
+        return CollectionUtils.isNotEmpty(loanRepository
+                .findAllNonClosedLoansBehindByLoanIds(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE), loanIds));
+    }
+
     private List<Long> calculateRelevantLoanIds(String pathInfo) {
 
         boolean isGlim = isGlim(pathInfo);
diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java
index f37895f34..0ccf932f3 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBApiFilterTest.java
@@ -18,6 +18,8 @@
  */
 package org.apache.fineract.infrastructure.jobs.filter;
 
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -27,15 +29,22 @@ import com.sun.research.ws.wadl.HTTPMethods;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.ZoneId;
 import java.util.Collections;
+import java.util.HashMap;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
 import org.apache.fineract.cob.service.LoanAccountLockService;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
+import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.apache.http.HttpStatus;
 import org.junit.jupiter.api.Assertions;
@@ -63,6 +72,8 @@ class LoanCOBApiFilterTest {
     private PlatformSecurityContext context;
     @Mock
     private InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService;
+    @Mock
+    private LoanRepository loanRepository;
 
     @Test
     void shouldLoanAndExternalMatchToo() {
@@ -117,34 +128,52 @@ class LoanCOBApiFilterTest {
     }
 
     @Test
-    void shouldProceedWhenLoanIsNotLocked() throws ServletException, IOException {
+    void shouldProceedWhenLoanIsNotLockedAndNoLoanIsBehind() throws ServletException, IOException {
         MockHttpServletRequest request = mock(MockHttpServletRequest.class);
         MockHttpServletResponse response = mock(MockHttpServletResponse.class);
         FilterChain filterChain = mock(FilterChain.class);
         AppUser appUser = mock(AppUser.class);
+        ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
+        HashMap<BusinessDateType, LocalDate> businessDates = new HashMap<>();
+        LocalDate businessDate = LocalDate.now(ZoneId.systemDefault());
+        businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate);
+        businessDates.put(BusinessDateType.COB_DATE, businessDate.minusDays(1));
+        ThreadLocalContextUtil.setBusinessDates(businessDates);
 
         given(request.getPathInfo()).willReturn("/loans/2/charges");
         given(request.getMethod()).willReturn(HTTPMethods.POST.value());
         given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
         given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
         given(context.authenticatedUser()).willReturn(appUser);
+        given(loanRepository.findAllNonClosedLoansBehindByLoanIds(
+                eq(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)), anyList()))
+                        .willReturn(Collections.emptyList());
 
         testObj.doFilterInternal(request, response, filterChain);
         verify(filterChain, times(1)).doFilter(request, response);
     }
 
     @Test
-    void shouldProceedWhenExternalLoanIsNotLocked() throws ServletException, IOException {
+    void shouldProceedWhenExternalLoanIsNotLockedAndNotBehind() throws ServletException, IOException {
         MockHttpServletRequest request = mock(MockHttpServletRequest.class);
         MockHttpServletResponse response = mock(MockHttpServletResponse.class);
         FilterChain filterChain = mock(FilterChain.class);
         AppUser appUser = mock(AppUser.class);
+        ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
+        HashMap<BusinessDateType, LocalDate> businessDates = new HashMap<>();
+        LocalDate businessDate = LocalDate.now(ZoneId.systemDefault());
+        businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate);
+        businessDates.put(BusinessDateType.COB_DATE, businessDate.minusDays(1));
+        ThreadLocalContextUtil.setBusinessDates(businessDates);
 
         given(request.getPathInfo()).willReturn("/loans/external-id/2/charges");
         given(request.getMethod()).willReturn(HTTPMethods.POST.value());
         given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
         given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
         given(context.authenticatedUser()).willReturn(appUser);
+        given(loanRepository.findAllNonClosedLoansBehindByLoanIds(
+                eq(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)), anyList()))
+                        .willReturn(Collections.emptyList());
 
         testObj.doFilterInternal(request, response, filterChain);
         verify(filterChain, times(1)).doFilter(request, response);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
index 71cd3cc39..984cc6959 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/inlinecob/InlineLoanCOBTest.java
@@ -230,7 +230,7 @@ public class InlineLoanCOBTest {
     }
 
     @Test
-    public void testInlineCOBOnRepayment() {
+    public void testInlineCOBOnRepaymentWithSoftLockedLoan() {
         GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
         BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
         GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec, "10", "0");
@@ -280,6 +280,55 @@ public class InlineLoanCOBTest {
         Assertions.assertEquals(LocalDate.of(2020, 3, 9), loan.getLastClosedBusinessDate());
     }
 
+    @Test
+    public void testInlineCOBCatchUpOnRepaymentWithNotLockedLoan() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
+        GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec, "10", "0");
+        loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
+        loanAccountLockHelper = new LoanAccountLockHelper(requestSpec, new ResponseSpecBuilder().expectStatusCode(202).build());
+
+        final Integer clientID = ClientHelper.createClient(requestSpec, responseSpec);
+        Assertions.assertNotNull(clientID);
+
+        Integer overdueFeeChargeId = ChargesHelper.createCharges(requestSpec, responseSpec,
+                ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
+        Assertions.assertNotNull(overdueFeeChargeId);
+
+        final Integer loanProductID = createLoanProduct(overdueFeeChargeId.toString());
+        Assertions.assertNotNull(loanProductID);
+        HashMap loanStatusHashMap;
+        final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2020");
+
+        Assertions.assertNotNull(loanID);
+
+        loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+        LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+        loanStatusHashMap = loanTransactionHelper.approveLoan("01 March 2020", loanID);
+        LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+        String loanDetails = loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+        loanStatusHashMap = loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020", loanID,
+                JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+        LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 3, 2));
+        inlineLoanCOBHelper.executeInlineCOB(List.of(loanID.longValue()));
+        GetLoansLoanIdResponse loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
+        Assertions.assertEquals(LocalDate.of(2020, 3, 2), loan.getLastClosedBusinessDate());
+
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 10));
+
+        createNewSimpleUserWithoutBypassPermission();
+        loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
+
+        loanTransactionHelper.makeRepayment("10 March 2020", 10.0f, loanID);
+
+        loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
+        Assertions.assertEquals(LocalDate.of(2020, 3, 9), loan.getLastClosedBusinessDate());
+    }
+
     @Test
     public void testInlineCOBOnBatchAPI() {
         GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);