From 8d636fab2bd0cb7aabac2ef7b3aeaef6824f3780 Mon Sep 17 00:00:00 2001 From: Janos Haber Date: Wed, 8 Mar 2023 13:34:17 +0100 Subject: [PATCH] FINERACT-1724 - Reschedule Loan fix - [x] Reschedule loan COB API filter fix - [x] Unit test for filter --- .../jobs/filter/LoanCOBApiFilter.java | 17 ++++++-- .../LoanRescheduleRequestRepository.java | 4 ++ .../jobs/filter/LoanCOBApiFilterTest.java | 41 +++++++++++++++++++ 3 files changed, 58 insertions(+), 4 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 09ddddcff..d8e9a6773 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 @@ -48,6 +48,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepositor 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.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException; import org.apache.http.HttpStatus; import org.springframework.http.HttpMethod; @@ -64,12 +65,14 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte private final InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService; private final LoanRepository loanRepository; + private final LoanRescheduleRequestRepository loanRescheduleRequestRepository; + private static final List HTTP_METHODS = List.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE); public static final Pattern IGNORE_LOAN_PATH_PATTERN = Pattern.compile("\\/loans\\/catch-up"); - public static final Pattern LOAN_PATH_PATTERN = Pattern.compile("\\/?loans\\/(?:external-id\\/)?([^\\/\\?]+).*"); + public static final Pattern LOAN_PATH_PATTERN = Pattern.compile("\\/(?:reschedule)?loans\\/(?:external-id\\/)?([^\\/\\?]+).*"); - public static final Pattern LOAN_GLIMACCOUNT_PATH_PATTERN = Pattern.compile("\\/?loans\\/glimAccount\\/(\\d+).*"); + public static final Pattern LOAN_GLIMACCOUNT_PATH_PATTERN = Pattern.compile("\\/loans\\/glimAccount\\/(\\d+).*"); private static final Predicate URL_FUNCTION = s -> LOAN_PATH_PATTERN.matcher(s).find() || LOAN_GLIMACCOUNT_PATH_PATTERN.matcher(s).find(); private static final String JOB_NAME = "INLINE_LOAN_COB"; @@ -123,7 +126,7 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte } else { try { List loanIds = calculateRelevantLoanIds(request.getPathInfo()); - if (isLoanSoftLocked(loanIds) || isLoanBehind(loanIds)) { + if (!loanIds.isEmpty() && (isLoanSoftLocked(loanIds) || isLoanBehind(loanIds))) { executeInlineCob(loanIds); } proceed(filterChain, request, response); @@ -207,6 +210,8 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte if (isExternal(pathInfo)) { String externalId = id; return loanRepository.findIdByExternalId(new ExternalId(externalId)); + } else if (isRescheduleLoans(pathInfo)) { + return loanRescheduleRequestRepository.getLoanIdByRescheduleRequestId(Long.valueOf(id)).orElse(null); } else if (StringUtils.isNumeric(id)) { return Long.valueOf(id); } else { @@ -221,6 +226,10 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte return LOAN_PATH_PATTERN.matcher(pathInfo).matches() && pathInfo.contains("external-id"); } + private boolean isRescheduleLoans(String pathInfo) { + return LOAN_PATH_PATTERN.matcher(pathInfo).matches() && pathInfo.contains("/rescheduleloans/"); + } + private boolean isOnApiList(String pathInfo, String method) { if (StringUtils.isBlank(pathInfo)) { return false; @@ -245,7 +254,7 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte } else { try { List result = calculateRelevantLoanIds("/" + batchRequest.getRelativeUrl()); - if (isLoanSoftLocked(result) || isLoanBehind(result)) { + if (!result.isEmpty() && (isLoanSoftLocked(result) || isLoanBehind(result))) { executeInlineCob(result); } return chain.serviceCall(batchRequest, uriInfo); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequestRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequestRepository.java index 64ed26bc0..4d93a9352 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequestRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanRescheduleRequestRepository.java @@ -18,10 +18,14 @@ */ package org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; public interface LoanRescheduleRequestRepository extends JpaRepository, JpaSpecificationExecutor { + @Query("select lrr.loan.id from LoanRescheduleRequest lrr where lrr.id = :rescheduleRequestId") + Optional getLoanIdByRescheduleRequestId(Long rescheduleRequestId); } 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 6043d748d..b787abffb 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 @@ -34,6 +34,7 @@ import java.time.LocalDate; import java.time.ZoneId; import java.util.Collections; import java.util.HashMap; +import java.util.Optional; import java.util.UUID; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -48,6 +49,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepositor 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.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; +import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.apache.fineract.useradministration.domain.AppUser; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Assertions; @@ -78,16 +81,25 @@ class LoanCOBApiFilterTest { @Mock private LoanRepository loanRepository; + @Mock + private LoanRescheduleRequestRepository loanRescheduleRequestRepository; + @Test void shouldLoanAndExternalMatchToo() { String externalId = UUID.randomUUID().toString(); Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/12").matches()); Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/12?correct=parameter").matches()); + Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/12?correct=parameter").matches()); + Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/rescheduleloans/12").matches()); + Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/rescheduleloans/12?correct=parameter").matches()); + Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/rescheduleloans/12?correct=parameter").matches()); Assertions.assertTrue(LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/external-id/" + externalId).matches()); Assertions.assertTrue( LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/external-id/" + externalId + "?additional=parameter").matches()); Assertions.assertEquals("12", LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/12").replaceAll("$1")); Assertions.assertEquals("12", LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/12?correct=parameter").replaceAll("$1")); + Assertions.assertEquals("12", LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/rescheduleloans/12").replaceAll("$1")); + Assertions.assertEquals("12", LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/rescheduleloans/12?correct=parameter").replaceAll("$1")); Assertions.assertEquals(externalId, LoanCOBApiFilter.LOAN_PATH_PATTERN.matcher("/loans/external-id/" + externalId).replaceAll("$1")); Assertions.assertEquals(externalId, @@ -210,6 +222,35 @@ class LoanCOBApiFilterTest { verify(filterChain, times(1)).doFilter(request, response); } + @Test + void shouldProceedWhenRescheduleLoanIsNotLockedAndNotBehind() 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 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); + Long resourceId = 123L; + given(request.getPathInfo()).willReturn("/rescheduleloans/" + resourceId + "/charges"); + given(request.getMethod()).willReturn(HTTPMethods.POST.value()); + given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false); + given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false); + LoanRescheduleRequest rescheduleRequest = mock(LoanRescheduleRequest.class); + given(loanRescheduleRequestRepository.getLoanIdByRescheduleRequestId(resourceId)).willReturn(Optional.of(2L)); + 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 shouldRunInlineCOBAndProceedWhenLoanIsSoftLocked() throws ServletException, IOException { MockHttpServletRequest request = mock(MockHttpServletRequest.class);