FINERACT-1678: Inline COB filter fix
This commit is contained in:
+1
-1
@@ -155,7 +155,7 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanIdAndLastClosedBusinessDate> getLoansToBeProcessed(List<Long> loanIds, LocalDate cobBusinessDate) {
|
private List<LoanIdAndLastClosedBusinessDate> getLoansToBeProcessed(List<Long> loanIds, LocalDate cobBusinessDate) {
|
||||||
return loanRepository.findAllNonClosedLoansBehindByLoanIds(cobBusinessDate, loanIds);
|
return loanRepository.findAllNonClosedLoansBehindOrNullByLoanIds(cobBusinessDate, loanIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) {
|
private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) {
|
||||||
|
|||||||
+3
-1
@@ -66,6 +66,7 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
|
|||||||
|
|
||||||
private static final List<HttpMethod> HTTP_METHODS = List.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE);
|
private static final List<HttpMethod> 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("\\/?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+).*");
|
||||||
@@ -224,7 +225,8 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
|
|||||||
if (StringUtils.isBlank(pathInfo)) {
|
if (StringUtils.isBlank(pathInfo)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return HTTP_METHODS.contains(HttpMethod.valueOf(method)) && URL_FUNCTION.test(pathInfo);
|
return HTTP_METHODS.contains(HttpMethod.valueOf(method)) && !IGNORE_LOAN_PATH_PATTERN.matcher(pathInfo).find()
|
||||||
|
&& URL_FUNCTION.test(pathInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isGlim(String pathInfo) {
|
private boolean isGlim(String pathInfo) {
|
||||||
|
|||||||
+8
-2
@@ -84,11 +84,13 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
|||||||
|
|
||||||
// should follow the logic of `FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE` query
|
// should follow the logic of `FIND_ALL_NON_CLOSED_LOANS_BY_LAST_CLOSED_BUSINESS_DATE` query
|
||||||
String FIND_OLDEST_COB_PROCESSED_LOAN = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.loanStatus in (100,200,300,303,304) and loan.lastClosedBusinessDate = (select min(l.lastClosedBusinessDate) from Loan l where l"
|
String FIND_OLDEST_COB_PROCESSED_LOAN = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.loanStatus in (100,200,300,303,304) and loan.lastClosedBusinessDate = (select min(l.lastClosedBusinessDate) from Loan l where l"
|
||||||
+ ".loanStatus in (100,200,300,303,304) and l.lastClosedBusinessDate <> :cobBusinessDate)";
|
+ ".loanStatus in (100,200,300,303,304) and l.lastClosedBusinessDate < :cobBusinessDate)";
|
||||||
|
|
||||||
String FIND_ALL_NON_CLOSED_LOANS_BEHIND_BY_LOAN_IDS = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in (100,200,300,303,304) and (loan.lastClosedBusinessDate <> :cobBusinessDate or "
|
String FIND_ALL_NON_CLOSED_LOANS_BEHIND_OR_NULL_BY_LOAN_IDS = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in (100,200,300,303,304) and (loan.lastClosedBusinessDate < :cobBusinessDate or "
|
||||||
+ "loan.lastClosedBusinessDate is null)";
|
+ "loan.lastClosedBusinessDate is null)";
|
||||||
|
|
||||||
|
String FIND_ALL_NON_CLOSED_LOANS_BEHIND_BY_LOAN_IDS = "select loan.id, loan.lastClosedBusinessDate from Loan loan where loan.id IN :loanIds and loan.loanStatus in (100,200,300,303,304) and loan.lastClosedBusinessDate < :cobBusinessDate";
|
||||||
|
|
||||||
String FIND_ALL_STAYED_LOCKED_BY_LOAN_IDS = "select loan.id, loan.externalId, loan.accountNumber from LoanAccountLock lock left join Loan loan on lock.loanId = loan.id where lock.lockPlacedOnCobBusinessDate = :cobBusinessDate";
|
String FIND_ALL_STAYED_LOCKED_BY_LOAN_IDS = "select loan.id, loan.externalId, loan.accountNumber from LoanAccountLock lock left join Loan loan on lock.loanId = loan.id where lock.lockPlacedOnCobBusinessDate = :cobBusinessDate";
|
||||||
|
|
||||||
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
||||||
@@ -194,6 +196,10 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
|||||||
List<LoanIdAndLastClosedBusinessDate> findAllNonClosedLoansBehindByLoanIds(@Param("cobBusinessDate") LocalDate cobBusinessDate,
|
List<LoanIdAndLastClosedBusinessDate> findAllNonClosedLoansBehindByLoanIds(@Param("cobBusinessDate") LocalDate cobBusinessDate,
|
||||||
@Param("loanIds") List<Long> loanIds);
|
@Param("loanIds") List<Long> loanIds);
|
||||||
|
|
||||||
|
@Query(FIND_ALL_NON_CLOSED_LOANS_BEHIND_OR_NULL_BY_LOAN_IDS)
|
||||||
|
List<LoanIdAndLastClosedBusinessDate> findAllNonClosedLoansBehindOrNullByLoanIds(@Param("cobBusinessDate") LocalDate cobBusinessDate,
|
||||||
|
@Param("loanIds") List<Long> loanIds);
|
||||||
|
|
||||||
@Query(FIND_OLDEST_COB_PROCESSED_LOAN)
|
@Query(FIND_OLDEST_COB_PROCESSED_LOAN)
|
||||||
List<LoanIdAndLastClosedBusinessDate> findOldestCOBProcessedLoan(@Param("cobBusinessDate") LocalDate cobBusinessDate);
|
List<LoanIdAndLastClosedBusinessDate> findOldestCOBProcessedLoan(@Param("cobBusinessDate") LocalDate cobBusinessDate);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ class InlineLoanCOBExecutorServiceImplTest {
|
|||||||
ThreadLocalContextUtil.setBusinessDates(businessDates);
|
ThreadLocalContextUtil.setBusinessDates(businessDates);
|
||||||
|
|
||||||
when(transactionTemplate.execute(any())).thenThrow(new LoanAccountLockCannotBeOverruledException(""));
|
when(transactionTemplate.execute(any())).thenThrow(new LoanAccountLockCannotBeOverruledException(""));
|
||||||
when(loanRepository.findAllNonClosedLoansBehindByLoanIds(any(), anyList())).thenReturn(List.of(loan));
|
when(loanRepository.findAllNonClosedLoansBehindOrNullByLoanIds(any(), anyList())).thenReturn(List.of(loan));
|
||||||
assertThrows(LoanAccountLockCannotBeOverruledException.class, () -> testObj.executeInlineJob(command, "INLINE_LOAN_COB"));
|
assertThrows(LoanAccountLockCannotBeOverruledException.class, () -> testObj.executeInlineJob(command, "INLINE_LOAN_COB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+97
@@ -37,6 +37,7 @@ import java.util.HashMap;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
|
||||||
import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
|
import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
|
||||||
import org.apache.fineract.cob.service.LoanAccountLockService;
|
import org.apache.fineract.cob.service.LoanAccountLockService;
|
||||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
@@ -227,6 +228,102 @@ class LoanCOBApiFilterTest {
|
|||||||
verify(filterChain, times(1)).doFilter(request, response);
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRunInlineCOBAndProceedWhenLoanIsBehind() 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);
|
||||||
|
|
||||||
|
LoanIdAndLastClosedBusinessDate result = mock(LoanIdAndLastClosedBusinessDate.class);
|
||||||
|
given(result.getId()).willReturn(2L);
|
||||||
|
given(request.getPathInfo()).willReturn("/loans/2?command=approve");
|
||||||
|
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
||||||
|
given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
|
||||||
|
given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
|
||||||
|
given(loanRepository.findAllNonClosedLoansBehindByLoanIds(
|
||||||
|
eq(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)), anyList()))
|
||||||
|
.willReturn(Collections.singletonList(result));
|
||||||
|
|
||||||
|
given(context.authenticatedUser()).willReturn(appUser);
|
||||||
|
|
||||||
|
testObj.doFilterInternal(request, response, filterChain);
|
||||||
|
verify(inlineLoanCOBExecutorService, times(1)).execute(Collections.singletonList(2L), "INLINE_LOAN_COB");
|
||||||
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRunInlineCOBAndProceedWhenLoanIsNotBehind() 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);
|
||||||
|
|
||||||
|
LoanIdAndLastClosedBusinessDate result = mock(LoanIdAndLastClosedBusinessDate.class);
|
||||||
|
given(result.getId()).willReturn(2L);
|
||||||
|
given(request.getPathInfo()).willReturn("/loans/2?command=approve");
|
||||||
|
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
||||||
|
given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
|
||||||
|
given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
|
||||||
|
given(loanRepository.findAllNonClosedLoansBehindByLoanIds(
|
||||||
|
eq(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)), anyList()))
|
||||||
|
.willReturn(Collections.emptyList());
|
||||||
|
|
||||||
|
given(context.authenticatedUser()).willReturn(appUser);
|
||||||
|
|
||||||
|
testObj.doFilterInternal(request, response, filterChain);
|
||||||
|
verify(inlineLoanCOBExecutorService, times(0)).execute(Collections.singletonList(2L), "INLINE_LOAN_COB");
|
||||||
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRunInlineCOBAndProceedWhenLoanIsBehindForLoanCreation() throws ServletException, IOException {
|
||||||
|
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
||||||
|
MockHttpServletResponse response = mock(MockHttpServletResponse.class);
|
||||||
|
FilterChain filterChain = mock(FilterChain.class);
|
||||||
|
AppUser appUser = mock(AppUser.class);
|
||||||
|
|
||||||
|
given(request.getPathInfo()).willReturn("/loans");
|
||||||
|
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
||||||
|
|
||||||
|
given(context.authenticatedUser()).willReturn(appUser);
|
||||||
|
|
||||||
|
testObj.doFilterInternal(request, response, filterChain);
|
||||||
|
verify(inlineLoanCOBExecutorService, times(0)).execute(Collections.singletonList(2L), "INLINE_LOAN_COB");
|
||||||
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRunInlineCOBForCatchUp() throws ServletException, IOException {
|
||||||
|
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
||||||
|
MockHttpServletResponse response = mock(MockHttpServletResponse.class);
|
||||||
|
FilterChain filterChain = mock(FilterChain.class);
|
||||||
|
AppUser appUser = mock(AppUser.class);
|
||||||
|
|
||||||
|
given(request.getPathInfo()).willReturn("/loans/catch-up");
|
||||||
|
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
||||||
|
|
||||||
|
given(context.authenticatedUser()).willReturn(appUser);
|
||||||
|
|
||||||
|
testObj.doFilterInternal(request, response, filterChain);
|
||||||
|
verify(inlineLoanCOBExecutorService, times(0)).execute(Collections.singletonList(2L), "INLINE_LOAN_COB");
|
||||||
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRejectWhenLoanIsHardLocked() throws ServletException, IOException {
|
void shouldRejectWhenLoanIsHardLocked() throws ServletException, IOException {
|
||||||
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user