[FINERACT-1678] Introducing Last COB Business Date for loans
This commit is contained in:
committed by
Arnold Gálovics
parent
6f7f55a227
commit
ca51084e86
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* 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.common;
|
||||||
|
|
||||||
|
import com.google.common.reflect.TypeToken;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.cob.exceptions.CustomJobParameterNotFoundException;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
|
||||||
|
import org.apache.fineract.infrastructure.springbatch.SpringBatchJobConstants;
|
||||||
|
import org.springframework.batch.core.StepContribution;
|
||||||
|
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component
|
||||||
|
public class CustomJobParameterResolver implements InitializingBean {
|
||||||
|
|
||||||
|
private final GoogleGsonSerializerHelper gsonFactory;
|
||||||
|
private final CustomJobParameterRepository customJobParameterRepository;
|
||||||
|
|
||||||
|
protected Gson gson;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
this.gson = gsonFactory.createSimpleGson();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resolve(StepContribution contribution, ChunkContext chunkContext, String customJobParameterKey,
|
||||||
|
String parameterNameInExecutionContext) {
|
||||||
|
Set<JobParameterDTO> jobParameterDTOList = getCustomJobParameterSet(chunkContext);
|
||||||
|
JobParameterDTO businessDateParameter = jobParameterDTOList.stream()
|
||||||
|
.filter(jobParameterDTO -> customJobParameterKey.equals(jobParameterDTO.getParameterName())) //
|
||||||
|
.findFirst().orElseThrow(() -> new CustomJobParameterNotFoundException(customJobParameterKey));
|
||||||
|
contribution.getStepExecution().getExecutionContext().put(parameterNameInExecutionContext,
|
||||||
|
businessDateParameter.getParameterValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<JobParameterDTO> getCustomJobParameterSet(ChunkContext chunkContext) {
|
||||||
|
Long customJobParameterId = (Long) chunkContext.getStepContext().getJobParameters()
|
||||||
|
.get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY);
|
||||||
|
CustomJobParameter customJobParameter = customJobParameterRepository.findById(customJobParameterId)
|
||||||
|
.orElseThrow(() -> new CustomJobParameterNotFoundException(customJobParameterId));
|
||||||
|
String parameterJson = customJobParameter.getParameterJson();
|
||||||
|
return gson.fromJson(parameterJson, new TypeToken<HashSet<JobParameterDTO>>() {}.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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.exceptions;
|
||||||
|
|
||||||
|
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||||
|
|
||||||
|
public class CustomJobParameterNotFoundException extends AbstractPlatformResourceNotFoundException {
|
||||||
|
|
||||||
|
public CustomJobParameterNotFoundException(String globalisationMessageCode, String defaultUserMessage,
|
||||||
|
Object... defaultUserMessageArgs) {
|
||||||
|
super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomJobParameterNotFoundException(Long customJobParameterId) {
|
||||||
|
super("error.msg.customjobparameter.not.found", "Custom job parameter not found with the given id: " + customJobParameterId,
|
||||||
|
customJobParameterId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomJobParameterNotFoundException(String customJobParameterName) {
|
||||||
|
super("error.msg.customjobparameter.not.found", "Custom job parameter not found with the given name: " + customJobParameterName,
|
||||||
|
customJobParameterName);
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-1
@@ -18,6 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.cob.loan;
|
package org.apache.fineract.cob.loan;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -40,12 +43,15 @@ public abstract class AbstractLoanItemProcessor implements ItemProcessor<Loan, L
|
|||||||
|
|
||||||
@Setter(AccessLevel.PROTECTED)
|
@Setter(AccessLevel.PROTECTED)
|
||||||
private ExecutionContext executionContext;
|
private ExecutionContext executionContext;
|
||||||
|
private LocalDate businessDate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Loan process(@NotNull Loan item) throws Exception {
|
public Loan process(@NotNull Loan item) throws Exception {
|
||||||
TreeMap<Long, String> businessStepMap = (TreeMap<Long, String>) executionContext.get(LoanCOBConstant.BUSINESS_STEP_MAP);
|
TreeMap<Long, String> businessStepMap = (TreeMap<Long, String>) executionContext.get(LoanCOBConstant.BUSINESS_STEP_MAP);
|
||||||
|
|
||||||
return cobBusinessStepService.run(businessStepMap, item);
|
Loan alreadyProcessedLoan = cobBusinessStepService.run(businessStepMap, item);
|
||||||
|
alreadyProcessedLoan.setLastClosedBusinessDate(businessDate);
|
||||||
|
return alreadyProcessedLoan;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterStep
|
@AfterStep
|
||||||
@@ -53,4 +59,11 @@ public abstract class AbstractLoanItemProcessor implements ItemProcessor<Loan, L
|
|||||||
return ExitStatus.COMPLETED;
|
return ExitStatus.COMPLETED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setBusinessDate(StepExecution stepExecution) {
|
||||||
|
this.businessDate = LocalDate.parse(
|
||||||
|
Objects.requireNonNull(
|
||||||
|
(String) stepExecution.getJobExecution().getExecutionContext().get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME)),
|
||||||
|
DateTimeFormatter.ISO_DATE);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -18,8 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.cob.loan;
|
package org.apache.fineract.cob.loan;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.cob.domain.LoanAccountLock;
|
import org.apache.fineract.cob.domain.LoanAccountLock;
|
||||||
@@ -35,13 +37,18 @@ import org.springframework.batch.repeat.RepeatStatus;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class FetchAndLockLoanTasklet implements Tasklet {
|
public class FetchAndLockLoanTasklet implements Tasklet {
|
||||||
|
|
||||||
|
private static final Long NUMBER_OF_DAYS_BEHIND = 1L;
|
||||||
|
|
||||||
private final LoanAccountLockRepository loanAccountLockRepository;
|
private final LoanAccountLockRepository loanAccountLockRepository;
|
||||||
|
|
||||||
private final RetrieveLoanIdService retrieveLoanIdService;
|
private final RetrieveLoanIdService retrieveLoanIdService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
||||||
List<Long> allNonClosedLoanIds = retrieveLoanIdService.retrieveLoanIds();
|
String businessDateParameter = (String) contribution.getStepExecution().getJobExecution().getExecutionContext()
|
||||||
|
.get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME);
|
||||||
|
LocalDate businessDate = LocalDate.parse(Objects.requireNonNull(businessDateParameter));
|
||||||
|
List<Long> allNonClosedLoanIds = retrieveLoanIdService.retrieveLoanIdsNDaysBehind(NUMBER_OF_DAYS_BEHIND, businessDate);
|
||||||
if (allNonClosedLoanIds.isEmpty()) {
|
if (allNonClosedLoanIds.isEmpty()) {
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -31,5 +31,6 @@ public class InlineCOBLoanItemProcessor extends AbstractLoanItemProcessor {
|
|||||||
@BeforeStep
|
@BeforeStep
|
||||||
public void beforeStep(StepExecution stepExecution) {
|
public void beforeStep(StepExecution stepExecution) {
|
||||||
setExecutionContext(stepExecution.getJobExecution().getExecutionContext());
|
setExecutionContext(stepExecution.getJobExecution().getExecutionContext());
|
||||||
|
setBusinessDate(stepExecution);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public final class LoanCOBConstant {
|
|||||||
|
|
||||||
public static final String ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS = "alreadyLockedOrProcessedLoanIds";
|
public static final String ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS = "alreadyLockedOrProcessedLoanIds";
|
||||||
public static final String INLINE_LOAN_COB_JOB_NAME = "INLINE_LOAN_COB";
|
public static final String INLINE_LOAN_COB_JOB_NAME = "INLINE_LOAN_COB";
|
||||||
|
public static final String BUSINESS_DATE_PARAMETER_NAME = "BusinessDate";
|
||||||
|
|
||||||
private LoanCOBConstant() {
|
private LoanCOBConstant() {
|
||||||
|
|
||||||
|
|||||||
+25
-1
@@ -20,6 +20,7 @@ package org.apache.fineract.cob.loan;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.cob.COBBusinessStepService;
|
import org.apache.fineract.cob.COBBusinessStepService;
|
||||||
|
import org.apache.fineract.cob.common.CustomJobParameterResolver;
|
||||||
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
||||||
import org.apache.fineract.cob.listener.COBExecutionListenerRunner;
|
import org.apache.fineract.cob.listener.COBExecutionListenerRunner;
|
||||||
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
|
||||||
@@ -33,6 +34,7 @@ import org.springframework.batch.core.configuration.annotation.StepBuilderFactor
|
|||||||
import org.springframework.batch.core.explore.JobExplorer;
|
import org.springframework.batch.core.explore.JobExplorer;
|
||||||
import org.springframework.batch.core.launch.JobOperator;
|
import org.springframework.batch.core.launch.JobOperator;
|
||||||
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
||||||
|
import org.springframework.batch.core.listener.ExecutionContextPromotionListener;
|
||||||
import org.springframework.batch.integration.config.annotation.EnableBatchIntegration;
|
import org.springframework.batch.integration.config.annotation.EnableBatchIntegration;
|
||||||
import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory;
|
import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -72,6 +74,8 @@ public class LoanCOBManagerConfiguration {
|
|||||||
private LoanAccountLockRepository accountLockRepository;
|
private LoanAccountLockRepository accountLockRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private BusinessEventNotifierService businessEventNotifierService;
|
private BusinessEventNotifierService businessEventNotifierService;
|
||||||
|
@Autowired
|
||||||
|
private CustomJobParameterResolver customJobParameterResolver;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@JobScope
|
@JobScope
|
||||||
@@ -90,6 +94,12 @@ public class LoanCOBManagerConfiguration {
|
|||||||
return localStepBuilderFactory.get("Fetch and Lock loan accounts - Step").tasklet(fetchAndLockLoanTasklet()).build();
|
return localStepBuilderFactory.get("Fetch and Lock loan accounts - Step").tasklet(fetchAndLockLoanTasklet()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Step resolveCustomJobParametersStep() {
|
||||||
|
return localStepBuilderFactory.get("Resolve custom job parameters - Step").tasklet(resolveCustomJobParametersTasklet())
|
||||||
|
.listener(customJobParametersPromotionListener()).build();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Step stayedLockedStep() {
|
public Step stayedLockedStep() {
|
||||||
return localStepBuilderFactory.get("Stayed locked loan accounts - Step").tasklet(stayedLockedTasklet()).build();
|
return localStepBuilderFactory.get("Stayed locked loan accounts - Step").tasklet(stayedLockedTasklet()).build();
|
||||||
@@ -101,6 +111,12 @@ public class LoanCOBManagerConfiguration {
|
|||||||
return new FetchAndLockLoanTasklet(accountLockRepository, retrieveLoanIdService);
|
return new FetchAndLockLoanTasklet(accountLockRepository, retrieveLoanIdService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@JobScope
|
||||||
|
public ResolveLoanCOBCustomJobParametersTasklet resolveCustomJobParametersTasklet() {
|
||||||
|
return new ResolveLoanCOBCustomJobParametersTasklet(customJobParameterResolver);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@JobScope
|
@JobScope
|
||||||
public StayedLockedLoansTasklet stayedLockedTasklet() {
|
public StayedLockedLoansTasklet stayedLockedTasklet() {
|
||||||
@@ -111,8 +127,16 @@ public class LoanCOBManagerConfiguration {
|
|||||||
public Job loanCOBJob() {
|
public Job loanCOBJob() {
|
||||||
return jobBuilderFactory.get(JobName.LOAN_COB.name()) //
|
return jobBuilderFactory.get(JobName.LOAN_COB.name()) //
|
||||||
.listener(new COBExecutionListenerRunner(applicationContext, JobName.LOAN_COB.name())) //
|
.listener(new COBExecutionListenerRunner(applicationContext, JobName.LOAN_COB.name())) //
|
||||||
.start(fetchAndLockStep()).next(loanCOBStep()).next(stayedLockedStep()) //
|
.start(resolveCustomJobParametersStep()) //
|
||||||
|
.next(fetchAndLockStep()).next(loanCOBStep()).next(stayedLockedStep()) //
|
||||||
.incrementer(new RunIdIncrementer()) //
|
.incrementer(new RunIdIncrementer()) //
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ExecutionContextPromotionListener customJobParametersPromotionListener() {
|
||||||
|
ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
|
||||||
|
listener.setKeys(new String[] { LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME });
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,5 +31,6 @@ public class LoanItemProcessor extends AbstractLoanItemProcessor {
|
|||||||
@BeforeStep
|
@BeforeStep
|
||||||
public void beforeStep(StepExecution stepExecution) {
|
public void beforeStep(StepExecution stepExecution) {
|
||||||
setExecutionContext(stepExecution.getExecutionContext());
|
setExecutionContext(stepExecution.getExecutionContext());
|
||||||
|
setBusinessDate(stepExecution);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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.loan;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.fineract.cob.common.CustomJobParameterResolver;
|
||||||
|
import org.springframework.batch.core.StepContribution;
|
||||||
|
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||||
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ResolveLoanCOBCustomJobParametersTasklet implements Tasklet {
|
||||||
|
|
||||||
|
private final CustomJobParameterResolver customJobParameterResolver;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||||
|
customJobParameterResolver.resolve(contribution, chunkContext, LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
|
||||||
|
LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME);
|
||||||
|
return RepeatStatus.FINISHED;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-2
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.cob.loan;
|
package org.apache.fineract.cob.loan;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||||
@@ -28,7 +29,7 @@ public class RetrieveAllNonClosedLoanIdServiceImpl implements RetrieveLoanIdServ
|
|||||||
private final LoanRepository loanRepository;
|
private final LoanRepository loanRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Long> retrieveLoanIds() {
|
public List<Long> retrieveLoanIdsNDaysBehind(Long numberOfDays, LocalDate businessDate) {
|
||||||
return loanRepository.findAllNonClosedLoanIds();
|
return loanRepository.findAllNonClosedLoanIdsOneDayBehind(businessDate.minusDays(numberOfDays));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -18,9 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.cob.loan;
|
package org.apache.fineract.cob.loan;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface RetrieveLoanIdService {
|
public interface RetrieveLoanIdService {
|
||||||
|
|
||||||
List<Long> retrieveLoanIds();
|
List<Long> retrieveLoanIdsNDaysBehind(Long numberOfDays, LocalDate businessDate);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-4
@@ -33,11 +33,14 @@ import org.apache.fineract.cob.domain.LoanAccountLock;
|
|||||||
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
||||||
import org.apache.fineract.cob.domain.LockOwner;
|
import org.apache.fineract.cob.domain.LockOwner;
|
||||||
import org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
|
import org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
|
||||||
|
import org.apache.fineract.cob.loan.LoanCOBConstant;
|
||||||
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
|
||||||
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
|
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter;
|
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameter;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
|
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
|
||||||
import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
|
import org.apache.fineract.infrastructure.jobs.exception.JobNotFoundException;
|
||||||
@@ -141,11 +144,13 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
|
|||||||
private Map<String, JobParameter> getJobParametersMap(List<Long> loanIds) {
|
private Map<String, JobParameter> getJobParametersMap(List<Long> loanIds) {
|
||||||
// TODO: refactor for a more generic solution
|
// TODO: refactor for a more generic solution
|
||||||
String parameterJson = gson.toJson(loanIds);
|
String parameterJson = gson.toJson(loanIds);
|
||||||
CustomJobParameter customJobParameter = new CustomJobParameter();
|
CustomJobParameter loanIdsJobParameter = new CustomJobParameter();
|
||||||
customJobParameter.setParameterJson(parameterJson);
|
loanIdsJobParameter.setParameterJson(parameterJson);
|
||||||
Long customJobParameterId = customJobParameterRepository.saveAndFlush(customJobParameter).getId();
|
Long loanIdsJobParameterId = customJobParameterRepository.saveAndFlush(loanIdsJobParameter).getId();
|
||||||
Map<String, JobParameter> jobParameterMap = new HashMap<>();
|
Map<String, JobParameter> jobParameterMap = new HashMap<>();
|
||||||
jobParameterMap.put(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY, new JobParameter(customJobParameterId));
|
jobParameterMap.put(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY, new JobParameter(loanIdsJobParameterId));
|
||||||
|
String businessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE).toString();
|
||||||
|
jobParameterMap.put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, new JobParameter(businessDate));
|
||||||
return jobParameterMap;
|
return jobParameterMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -128,9 +128,11 @@ public class SchedulerJobApiResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}")
|
@Path("{" + SchedulerJobApiConstants.JOB_ID + "}")
|
||||||
@Operation(summary = "Run a Job", description = "Manually Execute Specific Job.")
|
@Operation(summary = "Run a Job", description = "Manually Execute Specific Job.")
|
||||||
|
@RequestBody(content = @Content(schema = @Schema(implementation = SchedulerJobApiResourceSwagger.ExecuteJobRequest.class)))
|
||||||
@ApiResponses({ @ApiResponse(responseCode = "200", description = "POST: jobs/1?command=executeJob") })
|
@ApiResponses({ @ApiResponse(responseCode = "200", description = "POST: jobs/1?command=executeJob") })
|
||||||
public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) @Parameter(description = "jobId") final Long jobId,
|
public Response executeJob(@PathParam(SchedulerJobApiConstants.JOB_ID) @Parameter(description = "jobId") final Long jobId,
|
||||||
@QueryParam(SchedulerJobApiConstants.COMMAND) @Parameter(description = "command") final String commandParam) {
|
@QueryParam(SchedulerJobApiConstants.COMMAND) @Parameter(description = "command") final String commandParam,
|
||||||
|
@Parameter(hidden = true) final String jsonRequestBody) {
|
||||||
// check the logged in user have permissions to execute scheduler jobs
|
// check the logged in user have permissions to execute scheduler jobs
|
||||||
Response response;
|
Response response;
|
||||||
if (fineractProperties.getMode().isBatchManagerEnabled()) {
|
if (fineractProperties.getMode().isBatchManagerEnabled()) {
|
||||||
@@ -142,7 +144,7 @@ public class SchedulerJobApiResource {
|
|||||||
}
|
}
|
||||||
response = Response.status(400).build();
|
response = Response.status(400).build();
|
||||||
if (is(commandParam, SchedulerJobApiConstants.COMMAND_EXECUTE_JOB)) {
|
if (is(commandParam, SchedulerJobApiConstants.COMMAND_EXECUTE_JOB)) {
|
||||||
this.jobRegisterService.executeJob(jobId);
|
jobRegisterService.executeJobWithParameters(jobId, jsonRequestBody);
|
||||||
response = Response.status(202).build();
|
response = Response.status(202).build();
|
||||||
} else {
|
} else {
|
||||||
throw new UnrecognizedQueryParamException(SchedulerJobApiConstants.COMMAND, commandParam);
|
throw new UnrecognizedQueryParamException(SchedulerJobApiConstants.COMMAND, commandParam);
|
||||||
|
|||||||
+13
@@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.infrastructure.jobs.data.JobDetailHistoryData;
|
import org.apache.fineract.infrastructure.jobs.data.JobDetailHistoryData;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sanyam on 12/8/17.
|
* Created by sanyam on 12/8/17.
|
||||||
@@ -100,4 +101,16 @@ final class SchedulerJobApiResourceSwagger {
|
|||||||
public List<JobDetailHistoryDataSwagger> pageItems;
|
public List<JobDetailHistoryDataSwagger> pageItems;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Schema(description = "ExecuteJobRequest")
|
||||||
|
public static final class ExecuteJobRequest {
|
||||||
|
|
||||||
|
private ExecuteJobRequest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Schema(example = "Update loan Summary")
|
||||||
|
public List<JobParameterDTO> jobParameters;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.jobs.data;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public class JobParameterDTO {
|
||||||
|
|
||||||
|
private String parameterName;
|
||||||
|
private String parameterValue;
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.jobs.service;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class JobParameterDataParser {
|
||||||
|
|
||||||
|
private final FromJsonHelper jsonHelper;
|
||||||
|
|
||||||
|
public Set<JobParameterDTO> parseExecution(String requestBody) {
|
||||||
|
if (Objects.isNull(requestBody)) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
JsonObject element = JsonParser.parseString(requestBody).getAsJsonObject();
|
||||||
|
if (Objects.isNull(element)) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
JsonArray jobParametersJsonArray = jsonHelper.extractJsonArrayNamed("jobParameters", element);
|
||||||
|
if (Objects.isNull(jobParametersJsonArray)) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
Set<JobParameterDTO> jobParameters = new HashSet<>();
|
||||||
|
for (final JsonElement jobParameterElement : jobParametersJsonArray) {
|
||||||
|
jobParameters.add(new JobParameterDTO(jobParameterElement.getAsJsonObject().get("parameterName").getAsString(),
|
||||||
|
jobParameterElement.getAsJsonObject().get("parameterValue").getAsString()));
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(jobParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -22,7 +22,7 @@ import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
|
|||||||
|
|
||||||
public interface JobRegisterService {
|
public interface JobRegisterService {
|
||||||
|
|
||||||
void executeJob(Long jobId);
|
void executeJobWithParameters(Long jobId, String jobParametersJson);
|
||||||
|
|
||||||
void rescheduleJob(Long jobId);
|
void rescheduleJob(Long jobId);
|
||||||
|
|
||||||
|
|||||||
+18
-8
@@ -20,15 +20,18 @@ package org.apache.fineract.infrastructure.jobs.service;
|
|||||||
|
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||||
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
|
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
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.ScheduledJobDetail;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.SchedulerDetail;
|
import org.apache.fineract.infrastructure.jobs.domain.SchedulerDetail;
|
||||||
import org.apache.fineract.infrastructure.jobs.exception.JobNodeIdMismatchingException;
|
import org.apache.fineract.infrastructure.jobs.exception.JobNodeIdMismatchingException;
|
||||||
@@ -61,6 +64,8 @@ import org.springframework.stereotype.Service;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class JobRegisterServiceImpl implements JobRegisterService, ApplicationListener<ContextClosedEvent> {
|
public class JobRegisterServiceImpl implements JobRegisterService, ApplicationListener<ContextClosedEvent> {
|
||||||
|
|
||||||
|
private static final String JOB_EXECUTION_FAILED_MESSAGE = "Job execution failed for job with name: ";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SchedularWritePlatformService schedularWritePlatformService;
|
private SchedularWritePlatformService schedularWritePlatformService;
|
||||||
|
|
||||||
@@ -81,12 +86,15 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
@Autowired
|
@Autowired
|
||||||
private JobStarter jobStarter;
|
private JobStarter jobStarter;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JobParameterDataParser dataParser;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private JobNameService jobNameService;
|
private JobNameService jobNameService;
|
||||||
|
|
||||||
private static final String JOB_STARTER_METHOD_NAME = "run";
|
private static final String JOB_STARTER_METHOD_NAME = "run";
|
||||||
|
|
||||||
public void executeJob(final ScheduledJobDetail scheduledJobDetail, String triggerType) {
|
public void executeJob(final ScheduledJobDetail scheduledJobDetail, String triggerType, Set<JobParameterDTO> jobParameterDTOSet) {
|
||||||
try {
|
try {
|
||||||
final JobDataMap jobDataMap = new JobDataMap();
|
final JobDataMap jobDataMap = new JobDataMap();
|
||||||
if (triggerType == null) {
|
if (triggerType == null) {
|
||||||
@@ -96,7 +104,7 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
jobDataMap.put(SchedulerServiceConstants.TENANT_IDENTIFIER, ThreadLocalContextUtil.getTenant().getTenantIdentifier());
|
jobDataMap.put(SchedulerServiceConstants.TENANT_IDENTIFIER, ThreadLocalContextUtil.getTenant().getTenantIdentifier());
|
||||||
final String schedulerName = getSchedulerName(scheduledJobDetail);
|
final String schedulerName = getSchedulerName(scheduledJobDetail);
|
||||||
final Scheduler scheduler = SCHEDULERS.get(schedulerName);
|
final Scheduler scheduler = SCHEDULERS.get(schedulerName);
|
||||||
final JobDetail jobDetail = createJobDetail(scheduledJobDetail);
|
final JobDetail jobDetail = createJobDetail(scheduledJobDetail, jobParameterDTOSet);
|
||||||
JobKey jobKey = jobDetail.getKey();
|
JobKey jobKey = jobDetail.getKey();
|
||||||
if (scheduler == null || !scheduler.checkExists(jobKey)) {
|
if (scheduler == null || !scheduler.checkExists(jobKey)) {
|
||||||
SchedulerStopListener schedulerStopListener = new SchedulerStopListener(this);
|
SchedulerStopListener schedulerStopListener = new SchedulerStopListener(this);
|
||||||
@@ -157,7 +165,7 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
for (final ScheduledJobDetail jobDetail : scheduledJobDetails) {
|
for (final ScheduledJobDetail jobDetail : scheduledJobDetails) {
|
||||||
if (jobDetail.isTriggerMisfired() || jobDetail.isMismatchedJob()) {
|
if (jobDetail.isTriggerMisfired() || jobDetail.isMismatchedJob()) {
|
||||||
if (jobDetail.isActiveSchedular()) {
|
if (jobDetail.isActiveSchedular()) {
|
||||||
executeJob(jobDetail, SchedulerServiceConstants.TRIGGER_TYPE_CRON);
|
executeJob(jobDetail, SchedulerServiceConstants.TRIGGER_TYPE_CRON, Collections.emptySet());
|
||||||
jobDetail.setMismatchedJob(false);
|
jobDetail.setMismatchedJob(false);
|
||||||
}
|
}
|
||||||
final String schedulerName = getSchedulerName(jobDetail);
|
final String schedulerName = getSchedulerName(jobDetail);
|
||||||
@@ -198,7 +206,8 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void executeJob(final Long jobId) {
|
public void executeJobWithParameters(final Long jobId, String jobParametersJson) {
|
||||||
|
Set<JobParameterDTO> jobParameterDTOSet = dataParser.parseExecution(jobParametersJson);
|
||||||
final ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId);
|
final ScheduledJobDetail scheduledJobDetail = this.schedularWritePlatformService.findByJobId(jobId);
|
||||||
if (scheduledJobDetail == null) {
|
if (scheduledJobDetail == null) {
|
||||||
throw new JobNotFoundException(String.valueOf(jobId));
|
throw new JobNotFoundException(String.valueOf(jobId));
|
||||||
@@ -206,7 +215,7 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
final String nodeIdStored = scheduledJobDetail.getNodeId().toString();
|
final String nodeIdStored = scheduledJobDetail.getNodeId().toString();
|
||||||
|
|
||||||
if (nodeIdStored.equals(fineractProperties.getNodeId()) || nodeIdStored.equals("0")) {
|
if (nodeIdStored.equals(fineractProperties.getNodeId()) || nodeIdStored.equals("0")) {
|
||||||
executeJob(scheduledJobDetail, null);
|
executeJob(scheduledJobDetail, null, jobParameterDTOSet);
|
||||||
scheduledJobDetail.setMismatchedJob(false);
|
scheduledJobDetail.setMismatchedJob(false);
|
||||||
this.schedularWritePlatformService.saveOrUpdate(scheduledJobDetail);
|
this.schedularWritePlatformService.saveOrUpdate(scheduledJobDetail);
|
||||||
} else {
|
} else {
|
||||||
@@ -234,7 +243,7 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
@Override
|
@Override
|
||||||
public void scheduleJob(final ScheduledJobDetail scheduledJobDetails) {
|
public void scheduleJob(final ScheduledJobDetail scheduledJobDetails) {
|
||||||
try {
|
try {
|
||||||
final JobDetail jobDetail = createJobDetail(scheduledJobDetails);
|
final JobDetail jobDetail = createJobDetail(scheduledJobDetails, Collections.emptySet());
|
||||||
scheduledJobDetails.setJobKey(getJobKeyAsString(jobDetail.getKey()));
|
scheduledJobDetails.setJobKey(getJobKeyAsString(jobDetail.getKey()));
|
||||||
if (!scheduledJobDetails.isActiveSchedular()) {
|
if (!scheduledJobDetails.isActiveSchedular()) {
|
||||||
scheduledJobDetails.setNextRunTime(null);
|
scheduledJobDetails.setNextRunTime(null);
|
||||||
@@ -315,7 +324,8 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
return schedulerFactoryBean.getScheduler();
|
return schedulerFactoryBean.getScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
private JobDetail createJobDetail(final ScheduledJobDetail scheduledJobDetail) throws Exception {
|
private JobDetail createJobDetail(final ScheduledJobDetail scheduledJobDetail, Set<JobParameterDTO> jobParameterDTOSet)
|
||||||
|
throws Exception {
|
||||||
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
|
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
|
||||||
|
|
||||||
JobNameData jobName = jobNameService.getJobByHumanReadableName(scheduledJobDetail.getJobName());
|
JobNameData jobName = jobNameService.getJobByHumanReadableName(scheduledJobDetail.getJobName());
|
||||||
@@ -328,7 +338,7 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
|
|||||||
jobDetailFactoryBean.setGroup(scheduledJobDetail.getGroupName());
|
jobDetailFactoryBean.setGroup(scheduledJobDetail.getGroupName());
|
||||||
jobDetailFactoryBean.setConcurrent(false);
|
jobDetailFactoryBean.setConcurrent(false);
|
||||||
|
|
||||||
jobDetailFactoryBean.setArguments(job, scheduledJobDetail, ThreadLocalContextUtil.getContext());
|
jobDetailFactoryBean.setArguments(job, scheduledJobDetail, ThreadLocalContextUtil.getContext(), jobParameterDTOSet);
|
||||||
jobDetailFactoryBean.afterPropertiesSet();
|
jobDetailFactoryBean.afterPropertiesSet();
|
||||||
return jobDetailFactoryBean.getObject();
|
return jobDetailFactoryBean.getObject();
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-4
@@ -18,14 +18,20 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.infrastructure.jobs.service;
|
package org.apache.fineract.infrastructure.jobs.service;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.fineract.infrastructure.core.domain.FineractContext;
|
import org.apache.fineract.infrastructure.core.domain.FineractContext;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository;
|
import org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository;
|
||||||
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
|
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.service.jobname.JobNameService;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.service.jobparameterprovider.JobParameterProvider;
|
||||||
import org.springframework.batch.core.Job;
|
import org.springframework.batch.core.Job;
|
||||||
import org.springframework.batch.core.JobParameter;
|
import org.springframework.batch.core.JobParameter;
|
||||||
import org.springframework.batch.core.JobParameters;
|
import org.springframework.batch.core.JobParameters;
|
||||||
@@ -45,14 +51,19 @@ public class JobStarter {
|
|||||||
private final JobExplorer jobExplorer;
|
private final JobExplorer jobExplorer;
|
||||||
private final JobLauncher jobLauncher;
|
private final JobLauncher jobLauncher;
|
||||||
private final JobParameterRepository jobParameterRepository;
|
private final JobParameterRepository jobParameterRepository;
|
||||||
|
private final List<JobParameterProvider> jobParameterProviders;
|
||||||
|
private final JobNameService jobNameService;
|
||||||
|
|
||||||
public void run(Job job, ScheduledJobDetail scheduledJobDetail, FineractContext fineractContext)
|
public void run(Job job, ScheduledJobDetail scheduledJobDetail, FineractContext fineractContext,
|
||||||
throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException, JobParametersInvalidException,
|
Set<JobParameterDTO> jobParameterDTOSet) throws JobInstanceAlreadyCompleteException, JobExecutionAlreadyRunningException,
|
||||||
JobRestartException {
|
JobParametersInvalidException, JobRestartException {
|
||||||
ThreadLocalContextUtil.init(fineractContext);
|
ThreadLocalContextUtil.init(fineractContext);
|
||||||
Map<String, JobParameter> jobParameterMap = getJobParameter(scheduledJobDetail);
|
Map<String, JobParameter> jobParameterMap = getJobParameter(scheduledJobDetail);
|
||||||
JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(job)
|
JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(job)
|
||||||
.addJobParameters(new JobParameters(jobParameterMap)).toJobParameters();
|
.addJobParameters(new JobParameters(jobParameterMap))
|
||||||
|
.addJobParameters(new JobParameters(provideCustomJobParameters(
|
||||||
|
jobNameService.getJobByHumanReadableName(scheduledJobDetail.getJobName()).getEnumStyleName(), jobParameterDTOSet)))
|
||||||
|
.toJobParameters();
|
||||||
jobLauncher.run(job, jobParameters);
|
jobLauncher.run(job, jobParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,4 +76,10 @@ public class JobStarter {
|
|||||||
}
|
}
|
||||||
return jobParameterMap;
|
return jobParameterMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, JobParameter> provideCustomJobParameters(String jobName, Set<JobParameterDTO> jobParameterDTOSet) {
|
||||||
|
Optional<JobParameterProvider> jobParameterProvider = jobParameterProviders.stream()
|
||||||
|
.filter(provider -> provider.canProvideParametersForJob(jobName)).findFirst();
|
||||||
|
return jobParameterProvider.map(parameterProvider -> parameterProvider.provide(jobParameterDTOSet)).orElse(Collections.emptyMap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ public class ExecuteAllDirtyJobsTasklet implements Tasklet {
|
|||||||
|
|
||||||
for (ScheduledJobDetail scheduledJobDetail : jobDetails) {
|
for (ScheduledJobDetail scheduledJobDetail : jobDetails) {
|
||||||
if (scheduledJobDetail.getNodeId().toString().equals(fineractProperties.getNodeId())) {
|
if (scheduledJobDetail.getNodeId().toString().equals(fineractProperties.getNodeId())) {
|
||||||
jobRegisterService.executeJob(scheduledJobDetail.getId());
|
jobRegisterService.executeJobWithParameters(scheduledJobDetail.getId(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.jobs.service.jobparameterprovider;
|
||||||
|
|
||||||
|
public abstract class AbstractJobParameterProvider implements JobParameterProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canProvideParametersForJob(String jobName) {
|
||||||
|
return jobName.equals(getJobName());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getJobName();
|
||||||
|
}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.jobs.service.jobparameterprovider;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
|
||||||
|
import org.springframework.batch.core.JobParameter;
|
||||||
|
|
||||||
|
public interface JobParameterProvider {
|
||||||
|
|
||||||
|
Map<String, JobParameter> provide(Set<JobParameterDTO> jobParameterDTOList);
|
||||||
|
|
||||||
|
boolean canProvideParametersForJob(String jobName);
|
||||||
|
}
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* 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.infrastructure.jobs.service.jobparameterprovider;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.cob.loan.LoanCOBConstant;
|
||||||
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
|
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.CustomJobParameter;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
|
||||||
|
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||||
|
import org.apache.fineract.infrastructure.springbatch.SpringBatchJobConstants;
|
||||||
|
import org.springframework.batch.core.JobParameter;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LoanCOBJobParameterProvider extends AbstractJobParameterProvider implements InitializingBean {
|
||||||
|
|
||||||
|
private final CustomJobParameterRepository customJobParameterRepository;
|
||||||
|
private final GoogleGsonSerializerHelper gsonFactory;
|
||||||
|
|
||||||
|
private Gson gson;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
this.gson = gsonFactory.createSimpleGson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Map<String, JobParameter> provide(Set<JobParameterDTO> jobParameterDTOSet) {
|
||||||
|
Map<String, JobParameter> jobParameterMap = new HashMap<>();
|
||||||
|
CustomJobParameter customJobParameter = new CustomJobParameter();
|
||||||
|
customJobParameter.setParameterJson(gson.toJson(getJobParameterDTOListWithCorrectBusinessDate(jobParameterDTOSet)));
|
||||||
|
CustomJobParameter savedCustomJobParameter = customJobParameterRepository.saveAndFlush(customJobParameter);
|
||||||
|
jobParameterMap.put(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY, new JobParameter(savedCustomJobParameter.getId()));
|
||||||
|
return jobParameterMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getJobName() {
|
||||||
|
return JobName.LOAN_COB.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<JobParameterDTO> getJobParameterDTOListWithCorrectBusinessDate(Set<JobParameterDTO> jobParameterDTOset) {
|
||||||
|
Set<JobParameterDTO> jobParameterDTOListWithCorrectBusinessDate = jobParameterDTOset.isEmpty() ? new HashSet<>()
|
||||||
|
: new HashSet<>(jobParameterDTOset);
|
||||||
|
Optional<JobParameterDTO> optionalBusinessDateJobParameter = jobParameterDTOListWithCorrectBusinessDate.stream()
|
||||||
|
.filter(jobParameterDTO -> LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME.equals(jobParameterDTO.getParameterName()))
|
||||||
|
.findFirst();
|
||||||
|
if (optionalBusinessDateJobParameter.isEmpty()) {
|
||||||
|
jobParameterDTOListWithCorrectBusinessDate.add(new JobParameterDTO(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
|
||||||
|
ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE).format(DateTimeFormatter.ISO_DATE)));
|
||||||
|
}
|
||||||
|
return jobParameterDTOListWithCorrectBusinessDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-4
@@ -227,10 +227,11 @@ public class LoansApiResource {
|
|||||||
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions",
|
"interestRateFrequencyTypeOptions", "fundOptions", "repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions",
|
||||||
"loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting",
|
"loanPurposeOptions", "loanCollateralOptions", "chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting",
|
||||||
"loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount", "interestRateDifferential",
|
"loanCounter", "loanProductCounter", "notes", "accountLinkingOptions", "linkedAccount", "interestRateDifferential",
|
||||||
"isFloatingInterestRate", "interestRatesPeriods", LoanApiConstants.canUseForTopup, LoanApiConstants.isTopup,
|
"isFloatingInterestRate", "interestRatesPeriods", "lastClosedBusinessDate", LoanApiConstants.canUseForTopup,
|
||||||
LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount, LoanApiConstants.clientActiveLoanOptions,
|
LoanApiConstants.isTopup, LoanApiConstants.loanIdToClose, LoanApiConstants.topupAmount,
|
||||||
LoanApiConstants.datatables, LoanProductConstants.RATES_PARAM_NAME, LoanApiConstants.MULTIDISBURSE_DETAILS_PARAMNAME,
|
LoanApiConstants.clientActiveLoanOptions, LoanApiConstants.datatables, LoanProductConstants.RATES_PARAM_NAME,
|
||||||
LoanApiConstants.EMI_AMOUNT_VARIATIONS_PARAMNAME, LoanApiConstants.COLLECTION_PARAMNAME));
|
LoanApiConstants.MULTIDISBURSE_DETAILS_PARAMNAME, LoanApiConstants.EMI_AMOUNT_VARIATIONS_PARAMNAME,
|
||||||
|
LoanApiConstants.COLLECTION_PARAMNAME));
|
||||||
|
|
||||||
private static final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
|
private static final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
|
||||||
private static final Set<String> GLIM_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("glimId", "groupId", "clientId",
|
private static final Set<String> GLIM_ACCOUNTS_DATA_PARAMETERS = new HashSet<>(Arrays.asList("glimId", "groupId", "clientId",
|
||||||
|
|||||||
+1
@@ -978,6 +978,7 @@ final class LoansApiResourceSwagger {
|
|||||||
public Boolean fraud;
|
public Boolean fraud;
|
||||||
@Schema(example = "250.000000")
|
@Schema(example = "250.000000")
|
||||||
public Double totalOverpaid;
|
public Double totalOverpaid;
|
||||||
|
public LocalDate lastClosedBusinessDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Schema(description = "GetLoansResponse")
|
@Schema(description = "GetLoansResponse")
|
||||||
|
|||||||
+6
-3
@@ -250,6 +250,7 @@ public class LoanAccountData {
|
|||||||
|
|
||||||
private CollectionData delinquent;
|
private CollectionData delinquent;
|
||||||
private DelinquencyRangeData delinquencyRange;
|
private DelinquencyRangeData delinquencyRange;
|
||||||
|
private LocalDate lastClosedBusinessDate;
|
||||||
|
|
||||||
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
|
public static LoanAccountData importInstanceIndividual(EnumOptionData loanTypeEnumOption, Long clientId, Long productId,
|
||||||
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
|
Long loanOfficerId, LocalDate submittedOnDate, Long fundId, BigDecimal principal, Integer numberOfRepayments,
|
||||||
@@ -648,7 +649,8 @@ public class LoanAccountData {
|
|||||||
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus,
|
final Boolean isVariableInstallmentsAllowed, Integer minimumGap, Integer maximumGap, final EnumOptionData subStatus,
|
||||||
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo,
|
||||||
final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment,
|
||||||
final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud) {
|
final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud,
|
||||||
|
LocalDate lastClosedBusinessDate) {
|
||||||
|
|
||||||
final CollectionData delinquent = CollectionData.template();
|
final CollectionData delinquent = CollectionData.template();
|
||||||
|
|
||||||
@@ -687,7 +689,8 @@ public class LoanAccountData {
|
|||||||
.setSubStatus(subStatus).setCanUseForTopup(canUseForTopup).setTopup(isTopup).setClosureLoanId(closureLoanId)
|
.setSubStatus(subStatus).setCanUseForTopup(canUseForTopup).setTopup(isTopup).setClosureLoanId(closureLoanId)
|
||||||
.setClosureLoanAccountNo(closureLoanAccountNo).setTopupAmount(topupAmount).setIsEqualAmortization(isEqualAmortization)
|
.setClosureLoanAccountNo(closureLoanAccountNo).setTopupAmount(topupAmount).setIsEqualAmortization(isEqualAmortization)
|
||||||
.setFixedPrincipalPercentagePerInstallment(fixedPrincipalPercentagePerInstallment).setDelinquent(delinquent)
|
.setFixedPrincipalPercentagePerInstallment(fixedPrincipalPercentagePerInstallment).setDelinquent(delinquent)
|
||||||
.setDelinquencyRange(delinquencyRange).setDisallowExpectedDisbursements(disallowExpectedDisbursements).setFraud(fraud);
|
.setDelinquencyRange(delinquencyRange).setDisallowExpectedDisbursements(disallowExpectedDisbursements).setFraud(fraud)
|
||||||
|
.setLastClosedBusinessDate(lastClosedBusinessDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -771,7 +774,7 @@ public class LoanAccountData {
|
|||||||
.setIsEqualAmortization(acc.isEqualAmortization)
|
.setIsEqualAmortization(acc.isEqualAmortization)
|
||||||
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment)
|
.setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment)
|
||||||
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
.setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements)
|
||||||
.setFraud(acc.fraud);
|
.setFraud(acc.fraud).setLastClosedBusinessDate(acc.getLastClosedBusinessDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||||
|
|||||||
+10
@@ -432,6 +432,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
@Column(name = "fixed_principal_percentage_per_installment", scale = 2, precision = 5, nullable = true)
|
@Column(name = "fixed_principal_percentage_per_installment", scale = 2, precision = 5, nullable = true)
|
||||||
private BigDecimal fixedPrincipalPercentagePerInstallment;
|
private BigDecimal fixedPrincipalPercentagePerInstallment;
|
||||||
|
|
||||||
|
@Column(name = "last_closed_business_date")
|
||||||
|
private LocalDate lastClosedBusinessDate;
|
||||||
|
|
||||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||||
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
final String transactionProcessingStrategyCode, final LoanProductRelatedDetail loanRepaymentScheduleDetail,
|
||||||
@@ -6920,4 +6923,11 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
return ageOfOverdueDays;
|
return ageOfOverdueDays;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LocalDate getLastClosedBusinessDate() {
|
||||||
|
return this.lastClosedBusinessDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastClosedBusinessDate(LocalDate lastClosedBusinessDate) {
|
||||||
|
this.lastClosedBusinessDate = lastClosedBusinessDate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -71,6 +71,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
|||||||
|
|
||||||
String FIND_ALL_NON_CLOSED = "select loan.id from Loan loan where loan.loanStatus in (100,200,300,303,304)";
|
String FIND_ALL_NON_CLOSED = "select loan.id from Loan loan where loan.loanStatus in (100,200,300,303,304)";
|
||||||
|
|
||||||
|
String FIND_ALL_NON_CLOSED_ONE_DAY_BEHIND = "select loan.id from Loan loan where loan.loanStatus in (100,200,300,303,304) and (:last_closed_business_date = loan.lastClosedBusinessDate or loan.lastClosedBusinessDate is NULL)";
|
||||||
|
|
||||||
String FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus = 300 and loan.client.id = :clientId";
|
String FIND_NON_CLOSED_LOAN_THAT_BELONGS_TO_CLIENT = "select loan from Loan loan where loan.id = :loanId and loan.loanStatus = 300 and loan.client.id = :clientId";
|
||||||
|
|
||||||
String FIND_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber";
|
String FIND_BY_ACCOUNT_NUMBER = "select loan from Loan loan where loan.accountNumber = :accountNumber";
|
||||||
@@ -172,4 +174,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
|||||||
|
|
||||||
@Query(FIND_ID_BY_EXTERNAL_ID)
|
@Query(FIND_ID_BY_EXTERNAL_ID)
|
||||||
Long findIdByExternalId(@Param("externalId") String externalId);
|
Long findIdByExternalId(@Param("externalId") String externalId);
|
||||||
|
|
||||||
|
@Query(FIND_ALL_NON_CLOSED_ONE_DAY_BEHIND)
|
||||||
|
List<Long> findAllNonClosedLoanIdsOneDayBehind(@Param("last_closed_business_date") LocalDate businessDate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -661,7 +661,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
|
+ " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, "
|
||||||
+ " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, "
|
+ " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, "
|
||||||
+ " l.total_recovered_derived as totalRecovered, topuploan.account_no as closureLoanAccountNo, "
|
+ " l.total_recovered_derived as totalRecovered, topuploan.account_no as closureLoanAccountNo, "
|
||||||
+ " topup.topup_amount as topupAmount from m_loan l" //
|
+ " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate from m_loan l" //
|
||||||
+ " join m_product_loan lp on lp.id = l.product_id" //
|
+ " join m_product_loan lp on lp.id = l.product_id" //
|
||||||
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc."
|
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc."
|
||||||
+ sqlGenerator.escape("code") + " = l.currency_code" //
|
+ sqlGenerator.escape("code") + " = l.currency_code" //
|
||||||
@@ -987,6 +987,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
DelinquencyRangeData delinquencyRange = this.delinquencyReadPlatformService.retrieveCurrentDelinquencyTag(id);
|
DelinquencyRangeData delinquencyRange = this.delinquencyReadPlatformService.retrieveCurrentDelinquencyTag(id);
|
||||||
|
|
||||||
final boolean isFraud = rs.getBoolean("isFraud");
|
final boolean isFraud = rs.getBoolean("isFraud");
|
||||||
|
final LocalDate lastClosedBusinessDate = JdbcSupport.getLocalDate(rs, "lastClosedBusinessDate");
|
||||||
|
|
||||||
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
|
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName,
|
||||||
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
|
clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription,
|
||||||
@@ -1002,7 +1003,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
|||||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
||||||
createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus,
|
createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus,
|
||||||
canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization,
|
||||||
fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud);
|
fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud,
|
||||||
|
lastClosedBusinessDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,5 +93,5 @@
|
|||||||
<include file="parts/0071_add_external_id_support_for_loan_transaction.xml" relativeToChangelogFile="true"/>
|
<include file="parts/0071_add_external_id_support_for_loan_transaction.xml" relativeToChangelogFile="true"/>
|
||||||
<include file="parts/0072_add_result_and status_to_command_source.xml" relativeToChangelogFile="true" />
|
<include file="parts/0072_add_result_and status_to_command_source.xml" relativeToChangelogFile="true" />
|
||||||
<include file="parts/0073_add_result_status_code_to_command_source.xml" relativeToChangelogFile="true" />
|
<include file="parts/0073_add_result_status_code_to_command_source.xml" relativeToChangelogFile="true" />
|
||||||
|
<include file="parts/0074_add_last_cob_business_date_column_to_loan.xml" relativeToChangelogFile="true"/>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
|
||||||
|
<changeSet author="fineract" id="1">
|
||||||
|
<addColumn tableName="m_loan">
|
||||||
|
<column name="last_closed_business_date" type="DATE"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
||||||
+16
-6
@@ -19,16 +19,22 @@
|
|||||||
package org.apache.fineract.cob.loan;
|
package org.apache.fineract.cob.loan;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.Mockito.lenient;
|
import static org.mockito.Mockito.lenient;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import io.cucumber.java8.En;
|
import io.cucumber.java8.En;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.apache.fineract.cob.domain.LoanAccountLock;
|
import org.apache.fineract.cob.domain.LoanAccountLock;
|
||||||
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
||||||
import org.apache.fineract.cob.domain.LockOwner;
|
import org.apache.fineract.cob.domain.LockOwner;
|
||||||
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
@@ -50,23 +56,26 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
public FetchAndLockLoanStepDefinitions() {
|
public FetchAndLockLoanStepDefinitions() {
|
||||||
Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> {
|
Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> {
|
||||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||||
|
HashMap<BusinessDateType, LocalDate> businessDateMap = new HashMap<>();
|
||||||
|
businessDateMap.put(BusinessDateType.COB_DATE, LocalDate.now(ZoneId.systemDefault()));
|
||||||
|
ThreadLocalContextUtil.setBusinessDates(businessDateMap);
|
||||||
this.action = action;
|
this.action = action;
|
||||||
|
|
||||||
if ("empty loanIds".equals(action)) {
|
if ("empty loanIds".equals(action)) {
|
||||||
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(Collections.emptyList());
|
lenient().when(retrieveLoanIdService.retrieveLoanIdsNDaysBehind(anyLong(), any())).thenReturn(Collections.emptyList());
|
||||||
} else if ("good".equals(action)) {
|
} else if ("good".equals(action)) {
|
||||||
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(List.of(1L, 2L, 3L));
|
lenient().when(retrieveLoanIdService.retrieveLoanIdsNDaysBehind(anyLong(), any())).thenReturn(List.of(1L, 2L, 3L));
|
||||||
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList())).thenReturn(Collections.emptyList());
|
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList())).thenReturn(Collections.emptyList());
|
||||||
} else if ("soft lock".equals(action)) {
|
} else if ("soft lock".equals(action)) {
|
||||||
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(List.of(1L, 2L, 3L));
|
lenient().when(retrieveLoanIdService.retrieveLoanIdsNDaysBehind(anyLong(), any())).thenReturn(List.of(1L, 2L, 3L));
|
||||||
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList()))
|
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList()))
|
||||||
.thenReturn(List.of(new LoanAccountLock(1L, LockOwner.LOAN_COB_PARTITIONING)));
|
.thenReturn(List.of(new LoanAccountLock(1L, LockOwner.LOAN_COB_PARTITIONING)));
|
||||||
} else if ("inline cob".equals(action)) {
|
} else if ("inline cob".equals(action)) {
|
||||||
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(List.of(1L, 2L, 3L));
|
lenient().when(retrieveLoanIdService.retrieveLoanIdsNDaysBehind(anyLong(), any())).thenReturn(List.of(1L, 2L, 3L));
|
||||||
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList()))
|
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList()))
|
||||||
.thenReturn(List.of(new LoanAccountLock(2L, LockOwner.LOAN_INLINE_COB_PROCESSING)));
|
.thenReturn(List.of(new LoanAccountLock(2L, LockOwner.LOAN_INLINE_COB_PROCESSING)));
|
||||||
} else if ("chunk processing".equals(action)) {
|
} else if ("chunk processing".equals(action)) {
|
||||||
lenient().when(retrieveLoanIdService.retrieveLoanIds()).thenReturn(List.of(1L, 2L, 3L));
|
lenient().when(retrieveLoanIdService.retrieveLoanIdsNDaysBehind(anyLong(), any())).thenReturn(List.of(1L, 2L, 3L));
|
||||||
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList()))
|
lenient().when(loanAccountLockRepository.findAllByLoanIdIn(Mockito.anyList()))
|
||||||
.thenReturn(List.of(new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING)));
|
.thenReturn(List.of(new LoanAccountLock(3L, LockOwner.LOAN_COB_CHUNK_PROCESSING)));
|
||||||
}
|
}
|
||||||
@@ -74,7 +83,8 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
JobExecution jobExecution = new JobExecution(1L);
|
JobExecution jobExecution = new JobExecution(1L);
|
||||||
StepExecution stepExecution = new StepExecution("step", jobExecution);
|
StepExecution stepExecution = new StepExecution("step", jobExecution);
|
||||||
contribution = new StepContribution(stepExecution);
|
contribution = new StepContribution(stepExecution);
|
||||||
|
contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
|
||||||
|
LocalDate.now(ZoneId.systemDefault()).toString());
|
||||||
fetchAndLockLoanTasklet = new FetchAndLockLoanTasklet(loanAccountLockRepository, retrieveLoanIdService);
|
fetchAndLockLoanTasklet = new FetchAndLockLoanTasklet(loanAccountLockRepository, retrieveLoanIdService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -24,9 +24,12 @@ import static org.mockito.Mockito.lenient;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import io.cucumber.java8.En;
|
import io.cucumber.java8.En;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import org.apache.fineract.cob.COBBusinessStepService;
|
import org.apache.fineract.cob.COBBusinessStepService;
|
||||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||||
|
import org.springframework.batch.core.JobExecution;
|
||||||
import org.springframework.batch.core.StepExecution;
|
import org.springframework.batch.core.StepExecution;
|
||||||
import org.springframework.batch.item.ExecutionContext;
|
import org.springframework.batch.item.ExecutionContext;
|
||||||
|
|
||||||
@@ -47,8 +50,10 @@ public class LoanItemProcessorStepDefinitions implements En {
|
|||||||
|
|
||||||
public LoanItemProcessorStepDefinitions() {
|
public LoanItemProcessorStepDefinitions() {
|
||||||
Given("/^The LoanItemProcessor.process method with item (.*)$/", (String loanItem) -> {
|
Given("/^The LoanItemProcessor.process method with item (.*)$/", (String loanItem) -> {
|
||||||
|
JobExecution jobExecution = new JobExecution(1L);
|
||||||
StepExecution stepExecution = new StepExecution("test", null);
|
jobExecution.getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
|
||||||
|
LocalDate.now(ZoneId.systemDefault()).toString());
|
||||||
|
StepExecution stepExecution = new StepExecution("test", jobExecution);
|
||||||
ExecutionContext stepExecutionContext = new ExecutionContext();
|
ExecutionContext stepExecutionContext = new ExecutionContext();
|
||||||
stepExecutionContext.put(LoanCOBConstant.BUSINESS_STEP_MAP, treeMap);
|
stepExecutionContext.put(LoanCOBConstant.BUSINESS_STEP_MAP, treeMap);
|
||||||
stepExecution.setExecutionContext(stepExecutionContext);
|
stepExecution.setExecutionContext(stepExecutionContext);
|
||||||
|
|||||||
+2
-1
@@ -781,7 +781,9 @@ public class DelinquencyBucketsIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move the Business date to get older the loan and to have an overdue loan
|
// Move the Business date to get older the loan and to have an overdue loan
|
||||||
|
LocalDate lastLoanCOBBusinessDate = businessDate;
|
||||||
businessDate = businessDate.plusDays(50);
|
businessDate = businessDate.plusDays(50);
|
||||||
|
schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, businessDate, jobName, responseSpec);
|
||||||
log.info("Current date {}", businessDate);
|
log.info("Current date {}", businessDate);
|
||||||
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, businessDate);
|
||||||
// Run Second time the Job
|
// Run Second time the Job
|
||||||
@@ -838,5 +840,4 @@ public class DelinquencyBucketsIntegrationTest {
|
|||||||
log.info("{}", chargeApplyJSON);
|
log.info("{}", chargeApplyJSON);
|
||||||
return chargeApplyJSON;
|
return chargeApplyJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-3
@@ -46,6 +46,7 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
@@ -648,6 +649,58 @@ public class SchedulerJobsTestResults {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoanCOBRunsOnlyOnLoansOneDayBehind() {
|
||||||
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||||
|
loanTransactionHelper = new LoanTransactionHelper(requestSpec, responseSpec);
|
||||||
|
|
||||||
|
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));
|
||||||
|
String jobName = "Loan COB";
|
||||||
|
|
||||||
|
schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
|
GetLoansLoanIdResponse loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
|
||||||
|
Assertions.assertEquals(LocalDate.of(2020, 3, 2), loan.getLastClosedBusinessDate());
|
||||||
|
|
||||||
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 3, 3));
|
||||||
|
schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
|
|
||||||
|
loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
|
||||||
|
Assertions.assertEquals(LocalDate.of(2020, 3, 3), loan.getLastClosedBusinessDate());
|
||||||
|
|
||||||
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 3, 5));
|
||||||
|
schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
|
|
||||||
|
loan = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
|
||||||
|
Assertions.assertEquals(LocalDate.of(2020, 3, 3), loan.getLastClosedBusinessDate());
|
||||||
|
|
||||||
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoanCOBApplyPenaltyOnDue() {
|
public void testLoanCOBApplyPenaltyOnDue() {
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||||
@@ -688,15 +741,17 @@ public class SchedulerJobsTestResults {
|
|||||||
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
||||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||||
|
|
||||||
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 4, 2));
|
LocalDate lastBusinessDateBeforeFastForward = LocalDate.of(2020, 4, 2);
|
||||||
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, lastBusinessDateBeforeFastForward);
|
||||||
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||||
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
Assertions.assertEquals(39.39f, (Float) repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
|
||||||
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
"Verifying From Penalty Charges due fot first Repayment after Successful completion of Scheduler Job");
|
||||||
|
|
||||||
GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec, "10", "1");
|
GlobalConfigurationHelper.updateValueForGlobalConfiguration(this.requestSpec, this.responseSpec, "10", "1");
|
||||||
|
LocalDate dateToFastForward = LocalDate.of(2020, 5, 2);
|
||||||
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, LocalDate.of(2020, 5, 2));
|
schedulerJobHelper.fastForwardTime(lastBusinessDateBeforeFastForward, dateToFastForward, jobName, responseSpec);
|
||||||
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, dateToFastForward);
|
||||||
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
this.schedulerJobHelper.executeAndAwaitJob(jobName);
|
||||||
repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
repaymentScheduleDataAfter = this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, loanID);
|
||||||
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(2).get("penaltyChargesDue"),
|
Assertions.assertEquals(0, (Integer) repaymentScheduleDataAfter.get(2).get("penaltyChargesDue"),
|
||||||
|
|||||||
+11
@@ -31,6 +31,7 @@ import io.restassured.specification.RequestSpecification;
|
|||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -39,6 +40,7 @@ import java.util.Map;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -203,6 +205,15 @@ public class SchedulerJobHelper {
|
|||||||
// JobDetailData anyway.
|
// JobDetailData anyway.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fastForwardTime(LocalDate lastBusinessDateBeforeFastForward, LocalDate dateToFastForward, String jobName,
|
||||||
|
ResponseSpecification responseSpec) {
|
||||||
|
while (lastBusinessDateBeforeFastForward.isBefore(dateToFastForward)) {
|
||||||
|
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.COB_DATE, lastBusinessDateBeforeFastForward);
|
||||||
|
executeAndAwaitJob(jobName);
|
||||||
|
lastBusinessDateBeforeFastForward = lastBusinessDateBeforeFastForward.plusDays(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Callable<Map<String, String>> jobLastRunHistorySupplier(int jobId) {
|
private Callable<Map<String, String>> jobLastRunHistorySupplier(int jobId) {
|
||||||
return () -> {
|
return () -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user