[FINERACT-1884] write API calls on loan makes Inline COB and catches up the loan to the current COB business date
This commit is contained in:
committed by
Arnold Gálovics
parent
187889539f
commit
3744ad54ee
+13
-3
@@ -31,18 +31,22 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.fineract.batch.domain.BatchRequest;
|
import org.apache.fineract.batch.domain.BatchRequest;
|
||||||
import org.apache.fineract.batch.domain.BatchResponse;
|
import org.apache.fineract.batch.domain.BatchResponse;
|
||||||
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.core.data.ApiGlobalErrorResponse;
|
import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
|
||||||
import org.apache.fineract.infrastructure.core.filters.BatchFilter;
|
import org.apache.fineract.infrastructure.core.filters.BatchFilter;
|
||||||
import org.apache.fineract.infrastructure.core.filters.BatchFilterChain;
|
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.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
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.fineract.useradministration.exception.UnAuthenticatedUserException;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
@@ -57,6 +61,7 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
|
|||||||
private final LoanAccountLockService loanAccountLockService;
|
private final LoanAccountLockService loanAccountLockService;
|
||||||
private final PlatformSecurityContext context;
|
private final PlatformSecurityContext context;
|
||||||
private final InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService;
|
private final InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService;
|
||||||
|
private final LoanRepository loanRepository;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -115,9 +120,9 @@ public class LoanCOBApiFilter extends OncePerRequestFilter implements BatchFilte
|
|||||||
proceed(filterChain, request, response);
|
proceed(filterChain, request, response);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
List<Long> result = calculateRelevantLoanIds(request.getPathInfo());
|
List<Long> loanIds = calculateRelevantLoanIds(request.getPathInfo());
|
||||||
if (isLoanSoftLocked(result)) {
|
if (isLoanSoftLocked(loanIds) || isLoanBehind(loanIds)) {
|
||||||
executeInlineCob(result);
|
executeInlineCob(loanIds);
|
||||||
}
|
}
|
||||||
proceed(filterChain, request, response);
|
proceed(filterChain, request, response);
|
||||||
} catch (LoanIdsHardLockedException e) {
|
} 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) {
|
private List<Long> calculateRelevantLoanIds(String pathInfo) {
|
||||||
|
|
||||||
boolean isGlim = isGlim(pathInfo);
|
boolean isGlim = isGlim(pathInfo);
|
||||||
|
|||||||
+31
-2
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.jobs.filter;
|
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.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
@@ -27,15 +29,22 @@ import com.sun.research.ws.wadl.HTTPMethods;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
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.core.domain.FineractPlatformTenant;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
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.fineract.useradministration.domain.AppUser;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
@@ -63,6 +72,8 @@ class LoanCOBApiFilterTest {
|
|||||||
private PlatformSecurityContext context;
|
private PlatformSecurityContext context;
|
||||||
@Mock
|
@Mock
|
||||||
private InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService;
|
private InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService;
|
||||||
|
@Mock
|
||||||
|
private LoanRepository loanRepository;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldLoanAndExternalMatchToo() {
|
void shouldLoanAndExternalMatchToo() {
|
||||||
@@ -117,34 +128,52 @@ class LoanCOBApiFilterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldProceedWhenLoanIsNotLocked() throws ServletException, IOException {
|
void shouldProceedWhenLoanIsNotLockedAndNoLoanIsBehind() throws ServletException, IOException {
|
||||||
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
||||||
MockHttpServletResponse response = mock(MockHttpServletResponse.class);
|
MockHttpServletResponse response = mock(MockHttpServletResponse.class);
|
||||||
FilterChain filterChain = mock(FilterChain.class);
|
FilterChain filterChain = mock(FilterChain.class);
|
||||||
AppUser appUser = mock(AppUser.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.getPathInfo()).willReturn("/loans/2/charges");
|
||||||
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
||||||
given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
|
given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
|
||||||
given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
|
given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
|
||||||
given(context.authenticatedUser()).willReturn(appUser);
|
given(context.authenticatedUser()).willReturn(appUser);
|
||||||
|
given(loanRepository.findAllNonClosedLoansBehindByLoanIds(
|
||||||
|
eq(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)), anyList()))
|
||||||
|
.willReturn(Collections.emptyList());
|
||||||
|
|
||||||
testObj.doFilterInternal(request, response, filterChain);
|
testObj.doFilterInternal(request, response, filterChain);
|
||||||
verify(filterChain, times(1)).doFilter(request, response);
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldProceedWhenExternalLoanIsNotLocked() throws ServletException, IOException {
|
void shouldProceedWhenExternalLoanIsNotLockedAndNotBehind() throws ServletException, IOException {
|
||||||
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
MockHttpServletRequest request = mock(MockHttpServletRequest.class);
|
||||||
MockHttpServletResponse response = mock(MockHttpServletResponse.class);
|
MockHttpServletResponse response = mock(MockHttpServletResponse.class);
|
||||||
FilterChain filterChain = mock(FilterChain.class);
|
FilterChain filterChain = mock(FilterChain.class);
|
||||||
AppUser appUser = mock(AppUser.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.getPathInfo()).willReturn("/loans/external-id/2/charges");
|
||||||
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
given(request.getMethod()).willReturn(HTTPMethods.POST.value());
|
||||||
given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
|
given(loanAccountLockService.isLoanHardLocked(2L)).willReturn(false);
|
||||||
given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
|
given(loanAccountLockService.isLoanSoftLocked(2L)).willReturn(false);
|
||||||
given(context.authenticatedUser()).willReturn(appUser);
|
given(context.authenticatedUser()).willReturn(appUser);
|
||||||
|
given(loanRepository.findAllNonClosedLoansBehindByLoanIds(
|
||||||
|
eq(ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE)), anyList()))
|
||||||
|
.willReturn(Collections.emptyList());
|
||||||
|
|
||||||
testObj.doFilterInternal(request, response, filterChain);
|
testObj.doFilterInternal(request, response, filterChain);
|
||||||
verify(filterChain, times(1)).doFilter(request, response);
|
verify(filterChain, times(1)).doFilter(request, response);
|
||||||
|
|||||||
+50
-1
@@ -230,7 +230,7 @@ public class InlineLoanCOBTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInlineCOBOnRepayment() {
|
public void testInlineCOBOnRepaymentWithSoftLockedLoan() {
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||||
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, LocalDate.of(2020, 3, 2));
|
||||||
GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec, "10", "0");
|
GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec, "10", "0");
|
||||||
@@ -280,6 +280,55 @@ public class InlineLoanCOBTest {
|
|||||||
Assertions.assertEquals(LocalDate.of(2020, 3, 9), loan.getLastClosedBusinessDate());
|
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
|
@Test
|
||||||
public void testInlineCOBOnBatchAPI() {
|
public void testInlineCOBOnBatchAPI() {
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||||
|
|||||||
Reference in New Issue
Block a user