FINERACT-2421: Allow ProgressiveLoanInterestScheduleModel recalculation
Co-authored-by: Soma Sörös <soma.soros@dpc.hu>
This commit is contained in:
+7
@@ -5926,6 +5926,13 @@ public class LoanStepDef extends AbstractStepDef {
|
||||
assertEquals(Integer.valueOf(numberOfRelations), relationshipOptional.size(), "Missed relationship for transaction");
|
||||
}
|
||||
|
||||
@When("Call Internal API to remove progressive loan model by loan Id")
|
||||
public void callInternalAPIToRemoveProgressiveLoanModelByLoanId() {
|
||||
final PostLoansResponse loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
|
||||
final long loanId = loanCreateResponse.getLoanId();
|
||||
ok(() -> fineractClient.progressiveLoan().deleteModel(loanId));
|
||||
}
|
||||
|
||||
public static AdvancedPaymentData editPaymentAllocationFutureInstallment(String transactionType, String futureInstallmentAllocationRule,
|
||||
List<PaymentAllocationOrder> paymentAllocationOrder) {
|
||||
AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData();
|
||||
|
||||
@@ -1619,6 +1619,7 @@ Feature: LoanAccrualTransaction
|
||||
| 01 January 2024 | Disbursement | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | 100.0 | false | false |
|
||||
# --- Early repayment with 17.01 EUR on 15 Jan ---
|
||||
When Admin sets the business date to "15 January 2024"
|
||||
When Call Internal API to remove progressive loan model by loan Id
|
||||
When Admin makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "15 January 2024" with 17.01 EUR transaction amount
|
||||
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
@@ -1660,6 +1661,7 @@ Feature: LoanAccrualTransaction
|
||||
| 01 May 2024 | Accrual Activity | 0.48 | 0.0 | 0.48 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
| 31 May 2024 | Accrual | 1.87 | 0.0 | 1.87 | 0.0 | 0.0 | 0.0 | false | false |
|
||||
When Admin sets the business date to "02 June 2024"
|
||||
When Call Internal API to remove progressive loan model by loan Id
|
||||
And Admin runs inline COB job for Loan
|
||||
Then Loan Repayment schedule has 6 periods, with the following data for periods:
|
||||
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
|
||||
|
||||
+6
@@ -279,4 +279,10 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
@Query(FIND_ALL_LOANS_BEHIND_ON_DISBURSEMENT_DATE)
|
||||
List<COBIdAndLastClosedBusinessDate> findAllLoansBehindOnDisbursementDate(@Param("cobBusinessDate") LocalDate cobBusinessDate,
|
||||
@Param("loanIds") List<Long> loanIds, @Param("loanStatuses") Collection<LoanStatus> loanStatuses);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(l) > 0 THEN TRUE ELSE FALSE END FROM Loan l WHERE l.id = :loanId and l.loanRepaymentScheduleDetail.loanScheduleType = org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType.PROGRESSIVE")
|
||||
Boolean isProgressiveLoan(@Param("loanId") Long loanId);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(l) > 0 THEN TRUE ELSE FALSE END FROM Loan l WHERE l.id = :loanId and l.loanStatus in :allowedLoanStatuses")
|
||||
Boolean isLoanInAllowedStatus(@Param("loanId") Long loanId, @Param("allowedLoanStatuses") List<LoanStatus> allowedLoanStatuses);
|
||||
}
|
||||
|
||||
+4
@@ -313,4 +313,8 @@ public class LoanRepositoryWrapper {
|
||||
public boolean isEnabledCapitalizedIncome(Long loanId) {
|
||||
return repository.isEnabledCapitalizedIncome(loanId);
|
||||
}
|
||||
|
||||
public boolean isLoanInAllowedStatus(Long loanId, List<LoanStatus> allowedLoanStatuses) {
|
||||
return repository.isLoanInAllowedStatus(loanId, allowedLoanStatuses);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -83,5 +83,10 @@ public class EmbeddableProgressiveLoanScheduleGenerator {
|
||||
public Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, LocalDate businessDate) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long removeByLoanId(Long loanId) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -52,4 +52,7 @@ public class ProgressiveLoanModel extends AbstractPersistableCustom<Long> {
|
||||
@Column(name = "last_modified_on_utc", nullable = false)
|
||||
private OffsetDateTime lastModifiedDate;
|
||||
|
||||
@Column(name = "json_model_version", nullable = false)
|
||||
private String jsonModelVersion;
|
||||
|
||||
}
|
||||
|
||||
+7
@@ -23,6 +23,8 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ProgressiveLoanModel;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
public interface ProgressiveLoanModelRepository
|
||||
extends JpaSpecificationExecutor<ProgressiveLoanModel>, JpaRepository<ProgressiveLoanModel, Long> {
|
||||
@@ -30,4 +32,9 @@ public interface ProgressiveLoanModelRepository
|
||||
Optional<ProgressiveLoanModel> findOneByLoanId(Long loanId);
|
||||
|
||||
Optional<ProgressiveLoanModel> findOneByLoan(Loan loan);
|
||||
|
||||
Long removeByLoanId(Long loanId);
|
||||
|
||||
@Query("SELECT CASE WHEN COUNT(plm) > 0 THEN TRUE ELSE FALSE END FROM ProgressiveLoanModel plm WHERE plm.loan.id = :loanId AND plm.jsonModelVersion = :modelVersion")
|
||||
Boolean hasValidModel(@Param("loanId") Long loanId, @Param("modelVersion") String modelVersion);
|
||||
}
|
||||
|
||||
+2
@@ -41,4 +41,6 @@ public interface InterestScheduleModelRepositoryWrapper {
|
||||
boolean hasValidModelForDate(Long loanId, LocalDate targetDate);
|
||||
|
||||
Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, LocalDate businessDate);
|
||||
|
||||
Long removeByLoanId(Long loanId);
|
||||
}
|
||||
|
||||
+7
@@ -61,11 +61,13 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched
|
||||
ProgressiveLoanModel progressiveLoanModel = loanModelRepository.findOneByLoanId(loan.getId()).orElseGet(() -> {
|
||||
ProgressiveLoanModel plm = new ProgressiveLoanModel();
|
||||
plm.setLoan(loan);
|
||||
plm.setJsonModelVersion(ProgressiveLoanInterestScheduleModel.getModelVersion());
|
||||
return plm;
|
||||
});
|
||||
progressiveLoanModel.setBusinessDate(ThreadLocalContextUtil.getBusinessDate());
|
||||
progressiveLoanModel.setLastModifiedDate(DateUtils.getAuditOffsetDateTime());
|
||||
progressiveLoanModel.setJsonModel(jsonModel);
|
||||
progressiveLoanModel.setJsonModelVersion(ProgressiveLoanInterestScheduleModel.getModelVersion());
|
||||
loanModelRepository.save(progressiveLoanModel);
|
||||
});
|
||||
return model;
|
||||
@@ -133,4 +135,9 @@ public class InterestScheduleModelRepositoryWrapperImpl implements InterestSched
|
||||
.map(jsonModel -> progressiveLoanInterestScheduleModelParserService.fromJson(jsonModel, detail,
|
||||
MoneyHelper.getMathContext(), installmentAmountInMultipliesOf)); //
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long removeByLoanId(Long loanId) {
|
||||
return loanModelRepository.removeByLoanId(loanId);
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -22,6 +22,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
@@ -99,4 +100,13 @@ public class InternalProgressiveLoanApiResource implements InitializingBean {
|
||||
|
||||
return writePlatformService.writeInterestScheduleModel(loan, model);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{loanId}/model")
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
@Operation(summary = "Delete ProgressiveLoanInterestScheduleModel By Loan ID", description = "DO NOT USE THIS IN PRODUCTION!")
|
||||
@Transactional
|
||||
public Long deleteModel(@PathParam("loanId") @Parameter(description = "loanId") long loanId) {
|
||||
return writePlatformService.removeByLoanId(loanId);
|
||||
}
|
||||
}
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.portfolio.loanaccount.service;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.apache.fineract.portfolio.loanaccount.repository.ProgressiveLoanModelRepository;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ProgressiveLoanModelProcessingService {
|
||||
|
||||
private static final List<LoanStatus> allowedLoanStatuses = List.of(LoanStatus.ACTIVE, LoanStatus.CLOSED_OBLIGATIONS_MET,
|
||||
LoanStatus.CLOSED_WRITTEN_OFF, LoanStatus.OVERPAID);
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
private final ProgressiveLoanModelRecalculationService modelProcessingService;
|
||||
private final InterestScheduleModelRepositoryWrapper modelRepositoryWrapper;
|
||||
private final ProgressiveLoanModelRepository progressiveLoanModelRepository;
|
||||
|
||||
public boolean hasValidModel(Long loanId, String modelVersion) {
|
||||
return progressiveLoanModelRepository.hasValidModel(loanId, modelVersion);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void recalculateModelAndSave(Long loanId) {
|
||||
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
ProgressiveLoanInterestScheduleModel recalculatedModel = modelProcessingService.getRecalculatedModel(loan.getId(),
|
||||
ThreadLocalContextUtil.getBusinessDate());
|
||||
if (recalculatedModel != null) {
|
||||
modelRepositoryWrapper.writeInterestScheduleModel(loan, recalculatedModel);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean allowedLoanStatuses(Long loanId) {
|
||||
return loanRepositoryWrapper.isLoanInAllowedStatus(loanId, allowedLoanStatuses);
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.portfolio.loanaccount.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleTransactionProcessorFactory;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ProgressiveLoanModelRecalculationService {
|
||||
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory transactionProcessorFactory;
|
||||
private final LoanTransactionRepository loanTransactionRepository;
|
||||
private final LoanRepositoryWrapper loanRepositoryWrapper;
|
||||
|
||||
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
|
||||
public ProgressiveLoanInterestScheduleModel getRecalculatedModel(Long loanId, LocalDate tillDate) {
|
||||
Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
|
||||
LoanRepaymentScheduleTransactionProcessor transactionProcessor = transactionProcessorFactory
|
||||
.determineProcessor(loan.getTransactionProcessingStrategyCode());
|
||||
if (transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor advancedPaymentScheduleTransactionProcessor) {
|
||||
List<LoanTransaction> loanTransactions = loanTransactionRepository.findNonReversedTransactionsForReprocessingByLoan(loan);
|
||||
Pair<ChangedTransactionDetail, ProgressiveLoanInterestScheduleModel> result = advancedPaymentScheduleTransactionProcessor
|
||||
.reprocessProgressiveLoanTransactions(loan.getDisbursementDate(), tillDate, loanTransactions, loan.getCurrency(),
|
||||
loan.getRepaymentScheduleInstallments(), loan.getActiveCharges());
|
||||
return result.getRight();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -54,6 +54,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.ILoanConfigurationDetail
|
||||
@AllArgsConstructor
|
||||
public class ProgressiveLoanInterestScheduleModel {
|
||||
|
||||
private static final String modelVersion = "2";
|
||||
private final List<RepaymentPeriod> repaymentPeriods;
|
||||
private final TreeSet<InterestRate> interestRates;
|
||||
@JsonExclude
|
||||
@@ -419,4 +420,8 @@ public class ProgressiveLoanInterestScheduleModel {
|
||||
};
|
||||
}
|
||||
|
||||
public static String getModelVersion() {
|
||||
return modelVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -25,4 +25,5 @@
|
||||
<!-- Sequence is starting from 5000 to make it easier to move existing liquibase changesets here -->
|
||||
<include file="parts/5001_create_progressive_loan_model.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/5002_add_contract_termination_transaction.xml" relativeToChangelogFile="true"/>
|
||||
<include file="parts/5003_add_model_version.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?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.31.xsd"
|
||||
objectQuotingStrategy="QUOTE_ONLY_RESERVED_WORDS">
|
||||
<changeSet author="fineract" id="1">
|
||||
<addColumn tableName="m_loan_progressive_model">
|
||||
<column name="json_model_version" type="VARCHAR(100)" defaultValue="1">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+13
-2
@@ -32,6 +32,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.cob.COBBusinessStepService;
|
||||
import org.apache.fineract.cob.data.BusinessStepNameAndOrder;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.springframework.batch.core.ExitStatus;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.annotation.AfterStep;
|
||||
@@ -44,6 +46,7 @@ import org.springframework.lang.NonNull;
|
||||
public abstract class AbstractLoanItemProcessor implements ItemProcessor<Loan, Loan> {
|
||||
|
||||
private final COBBusinessStepService cobBusinessStepService;
|
||||
private final ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService;
|
||||
|
||||
@Setter(AccessLevel.PROTECTED)
|
||||
private ExecutionContext executionContext;
|
||||
@@ -51,18 +54,26 @@ public abstract class AbstractLoanItemProcessor implements ItemProcessor<Loan, L
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Override
|
||||
public Loan process(@NonNull Loan item) throws Exception {
|
||||
public Loan process(@NonNull Loan loan) throws Exception {
|
||||
if (needToRebuildModel(loan)) {
|
||||
progressiveLoanModelProcessingService.recalculateModelAndSave(loan.getId());
|
||||
}
|
||||
Set<BusinessStepNameAndOrder> businessSteps = (Set<BusinessStepNameAndOrder>) executionContext.get(LoanCOBConstant.BUSINESS_STEPS);
|
||||
if (businessSteps == null) {
|
||||
throw new IllegalStateException("No business steps found in the execution context");
|
||||
}
|
||||
TreeMap<Long, String> businessStepMap = getBusinessStepMap(businessSteps);
|
||||
|
||||
Loan alreadyProcessedLoan = cobBusinessStepService.run(businessStepMap, item);
|
||||
Loan alreadyProcessedLoan = cobBusinessStepService.run(businessStepMap, loan);
|
||||
alreadyProcessedLoan.setLastClosedBusinessDate(businessDate);
|
||||
return alreadyProcessedLoan;
|
||||
}
|
||||
|
||||
private boolean needToRebuildModel(Loan loan) {
|
||||
return loan.isProgressiveSchedule() && !progressiveLoanModelProcessingService.hasValidModel(loan.getId(),
|
||||
ProgressiveLoanInterestScheduleModel.getModelVersion());
|
||||
}
|
||||
|
||||
private TreeMap<Long, String> getBusinessStepMap(Set<BusinessStepNameAndOrder> businessSteps) {
|
||||
Map<Long, String> businessStepMap = businessSteps.stream()
|
||||
.collect(Collectors.toMap(BusinessStepNameAndOrder::getStepOrder, BusinessStepNameAndOrder::getStepName));
|
||||
|
||||
+4
-2
@@ -19,13 +19,15 @@
|
||||
package org.apache.fineract.cob.loan;
|
||||
|
||||
import org.apache.fineract.cob.COBBusinessStepService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.annotation.BeforeStep;
|
||||
|
||||
public class InlineCOBLoanItemProcessor extends AbstractLoanItemProcessor {
|
||||
|
||||
public InlineCOBLoanItemProcessor(COBBusinessStepService cobBusinessStepService) {
|
||||
super(cobBusinessStepService);
|
||||
public InlineCOBLoanItemProcessor(COBBusinessStepService cobBusinessStepService,
|
||||
ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService) {
|
||||
super(cobBusinessStepService, progressiveLoanModelProcessingService);
|
||||
}
|
||||
|
||||
@BeforeStep
|
||||
|
||||
+5
-1
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||
import org.apache.fineract.infrastructure.springbatch.PropertyService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||
@@ -80,6 +81,9 @@ public class LoanCOBWorkerConfiguration {
|
||||
@Autowired
|
||||
private LoanLockingService loanLockingService;
|
||||
|
||||
@Autowired
|
||||
private ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService;
|
||||
|
||||
@Bean(name = LoanCOBConstant.LOAN_COB_WORKER_STEP)
|
||||
public Step loanCOBWorkerStep(Flow cobFlow) {
|
||||
return stepBuilderFactory.get("Loan COB worker - Step").inputChannel(inboundRequests).flow(cobFlow).build();
|
||||
@@ -178,7 +182,7 @@ public class LoanCOBWorkerConfiguration {
|
||||
@Bean
|
||||
@StepScope
|
||||
public LoanItemProcessor cobWorkerItemProcessor() {
|
||||
return new LoanItemProcessor(cobBusinessStepService);
|
||||
return new LoanItemProcessor(cobBusinessStepService, progressiveLoanModelProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+4
-2
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||
import org.apache.fineract.infrastructure.springbatch.PropertyService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.annotation.JobScope;
|
||||
@@ -65,9 +66,10 @@ public class LoanInlineCOBConfig {
|
||||
private CustomJobParameterRepository customJobParameterRepository;
|
||||
@Autowired
|
||||
private CustomJobParameterResolver customJobParameterResolver;
|
||||
|
||||
@Autowired
|
||||
private LoanLockingService loanLockingService;
|
||||
@Autowired
|
||||
private ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService;
|
||||
|
||||
@Bean
|
||||
public InlineLoanCOBBuildExecutionContextTasklet inlineLoanCOBBuildExecutionContextTasklet() {
|
||||
@@ -106,7 +108,7 @@ public class LoanInlineCOBConfig {
|
||||
@JobScope
|
||||
@Bean
|
||||
public InlineCOBLoanItemProcessor inlineCobWorkerItemProcessor() {
|
||||
return new InlineCOBLoanItemProcessor(cobBusinessStepService);
|
||||
return new InlineCOBLoanItemProcessor(cobBusinessStepService, progressiveLoanModelProcessingService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -19,13 +19,15 @@
|
||||
package org.apache.fineract.cob.loan;
|
||||
|
||||
import org.apache.fineract.cob.COBBusinessStepService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.core.annotation.BeforeStep;
|
||||
|
||||
public class LoanItemProcessor extends AbstractLoanItemProcessor {
|
||||
|
||||
public LoanItemProcessor(COBBusinessStepService cobBusinessStepService) {
|
||||
super(cobBusinessStepService);
|
||||
public LoanItemProcessor(COBBusinessStepService cobBusinessStepService,
|
||||
ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService) {
|
||||
super(cobBusinessStepService, progressiveLoanModelProcessingService);
|
||||
}
|
||||
|
||||
@BeforeStep
|
||||
|
||||
+5
@@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.core.service.MDCWrapper;
|
||||
import org.apache.fineract.infrastructure.instancemode.filter.FineractInstanceModeApiFilter;
|
||||
import org.apache.fineract.infrastructure.jobs.filter.LoanCOBApiFilter;
|
||||
import org.apache.fineract.infrastructure.jobs.filter.LoanCOBFilterHelper;
|
||||
import org.apache.fineract.infrastructure.jobs.filter.ProgressiveLoanModelCheckerFilter;
|
||||
import org.apache.fineract.infrastructure.security.data.PlatformRequestLog;
|
||||
import org.apache.fineract.infrastructure.security.filter.TenantAwareBasicAuthenticationFilter;
|
||||
import org.apache.fineract.infrastructure.security.filter.TwoFactorAuthenticationFilter;
|
||||
@@ -113,6 +114,8 @@ public class SecurityConfig {
|
||||
private LoanCOBFilterHelper loanCOBFilterHelper;
|
||||
@Autowired
|
||||
private IdempotencyStoreHelper idempotencyStoreHelper;
|
||||
@Autowired
|
||||
ProgressiveLoanModelCheckerFilter progressiveLoanModelCheckerFilter;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
@@ -216,8 +219,10 @@ public class SecurityConfig {
|
||||
if (loanCOBFilterHelper != null) {
|
||||
http.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class).addFilterAfter(idempotencyStoreFilter(),
|
||||
LoanCOBApiFilter.class);
|
||||
http.addFilterBefore(progressiveLoanModelCheckerFilter, LoanCOBApiFilter.class);
|
||||
} else {
|
||||
http.addFilterAfter(idempotencyStoreFilter(), FineractInstanceModeApiFilter.class);
|
||||
http.addFilterAfter(progressiveLoanModelCheckerFilter, FineractInstanceModeApiFilter.class);
|
||||
}
|
||||
if (fineractProperties.getIpTracking().isEnabled()) {
|
||||
http.addFilterAfter(callerIpTrackingFilter(), RequestResponseFilter.class);
|
||||
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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.filter;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.http.BodyCachingHttpServletRequestWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ProgressiveLoanModelCheckerFilter extends OncePerRequestFilter {
|
||||
|
||||
private final LoanRepository loanRepository;
|
||||
private final ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService;
|
||||
private final ProgressiveLoanModelCheckerHelper helper;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
request = new BodyCachingHttpServletRequestWrapper(request);
|
||||
|
||||
if (!helper.isOnApiList((BodyCachingHttpServletRequestWrapper) request)) {
|
||||
proceed(filterChain, request, response);
|
||||
} else {
|
||||
List<Long> loanIds = helper.calculateRelevantLoanIds((BodyCachingHttpServletRequestWrapper) request);
|
||||
if (!loanIds.isEmpty()) {
|
||||
loanIds.forEach(loanId -> {
|
||||
if (isProgressiveLoan(loanId) && allowedLoanStatuses(loanId) && !hasValidModel(loanId)) {
|
||||
progressiveLoanModelProcessingService.recalculateModelAndSave(loanId);
|
||||
}
|
||||
});
|
||||
}
|
||||
proceed(filterChain, request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProgressiveLoan(Long loanId) {
|
||||
return loanRepository.isProgressiveLoan(loanId);
|
||||
}
|
||||
|
||||
private boolean allowedLoanStatuses(Long loanId) {
|
||||
return progressiveLoanModelProcessingService.allowedLoanStatuses(loanId);
|
||||
}
|
||||
|
||||
private boolean hasValidModel(Long loanId) {
|
||||
return progressiveLoanModelProcessingService.hasValidModel(loanId, ProgressiveLoanInterestScheduleModel.getModelVersion());
|
||||
}
|
||||
|
||||
private void proceed(FilterChain filterChain, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* 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.filter;
|
||||
|
||||
import static org.apache.fineract.batch.command.CommandStrategyUtils.isRelativeUrlVersioned;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.batch.domain.BatchRequest;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.http.BodyCachingHttpServletRequestWrapper;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonitoringAccount;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class ProgressiveLoanModelCheckerHelper implements InitializingBean {
|
||||
|
||||
private final GLIMAccountInfoRepository glimAccountInfoRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
|
||||
private final LoanRescheduleRequestRepository loanRescheduleRequestRepository;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private static final List<HttpMethod> HTTP_METHODS = List.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE);
|
||||
|
||||
public static final Pattern IGNORE_LOAN_PATH_PATTERN = Pattern.compile("/v[1-9][0-9]*/loans/catch-up");
|
||||
public static final Pattern LOAN_PATH_PATTERN = Pattern.compile("/v[1-9][0-9]*/(?:reschedule)?loans/(?:external-id/)?([^/?]+).*");
|
||||
|
||||
public static final Pattern LOAN_GLIMACCOUNT_PATH_PATTERN = Pattern.compile("/v[1-9][0-9]*/loans/glimAccount/(\\d+).*");
|
||||
private static final Predicate<String> URL_FUNCTION = s -> LOAN_PATH_PATTERN.matcher(s).find()
|
||||
|| LOAN_GLIMACCOUNT_PATH_PATTERN.matcher(s).find();
|
||||
|
||||
private Long getLoanId(boolean isGlim, String pathInfo) {
|
||||
if (!isGlim) {
|
||||
String id = LOAN_PATH_PATTERN.matcher(pathInfo).replaceAll("$1");
|
||||
if (isExternal(pathInfo)) {
|
||||
String externalId = id;
|
||||
return loanRepository.findIdByExternalId(new ExternalId(externalId));
|
||||
} else if (isRescheduleLoans(pathInfo)) {
|
||||
return loanRescheduleRequestRepository.getLoanIdByRescheduleRequestId(Long.valueOf(id)).orElse(null);
|
||||
} else if (StringUtils.isNumeric(id)) {
|
||||
return Long.valueOf(id);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return Long.valueOf(LOAN_GLIMACCOUNT_PATH_PATTERN.matcher(pathInfo).replaceAll("$1"));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExternal(String pathInfo) {
|
||||
return LOAN_PATH_PATTERN.matcher(pathInfo).matches() && pathInfo.contains("external-id");
|
||||
}
|
||||
|
||||
private boolean isRescheduleLoans(String pathInfo) {
|
||||
return LOAN_PATH_PATTERN.matcher(pathInfo).matches() && pathInfo.contains("/v1/rescheduleloans/");
|
||||
}
|
||||
|
||||
public boolean isOnApiList(BodyCachingHttpServletRequestWrapper request) throws IOException {
|
||||
String pathInfo = request.getPathInfo();
|
||||
String method = request.getMethod();
|
||||
if (StringUtils.isBlank(pathInfo)) {
|
||||
return false;
|
||||
}
|
||||
if (isBatchApi(pathInfo)) {
|
||||
return isBatchApiMatching(request);
|
||||
} else {
|
||||
return isApiMatching(method, pathInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBatchApiMatching(BodyCachingHttpServletRequestWrapper request) throws IOException {
|
||||
for (BatchRequest batchRequest : getBatchRequests(request)) {
|
||||
String method = batchRequest.getMethod();
|
||||
String pathInfo = batchRequest.getRelativeUrl();
|
||||
if (isApiMatching(method, pathInfo)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<BatchRequest> getBatchRequests(BodyCachingHttpServletRequestWrapper request) throws IOException {
|
||||
List<BatchRequest> batchRequests = objectMapper.readValue(request.getInputStream(), new TypeReference<>() {});
|
||||
// since we read body, we have to reset so the upcoming readings are successful
|
||||
request.resetStream();
|
||||
for (BatchRequest batchRequest : batchRequests) {
|
||||
String pathInfo = "/" + batchRequest.getRelativeUrl();
|
||||
if (!isRelativeUrlVersioned(batchRequest.getRelativeUrl())) {
|
||||
pathInfo = "/v1/" + batchRequest.getRelativeUrl();
|
||||
}
|
||||
batchRequest.setRelativeUrl(pathInfo);
|
||||
}
|
||||
return batchRequests;
|
||||
}
|
||||
|
||||
private boolean isApiMatching(String method, String pathInfo) {
|
||||
return HTTP_METHODS.contains(HttpMethod.valueOf(method)) && !IGNORE_LOAN_PATH_PATTERN.matcher(pathInfo).find()
|
||||
&& URL_FUNCTION.test(pathInfo);
|
||||
}
|
||||
|
||||
private boolean isBatchApi(String pathInfo) {
|
||||
return pathInfo.startsWith("/v1/batches");
|
||||
}
|
||||
|
||||
private boolean isGlim(String pathInfo) {
|
||||
return LOAN_GLIMACCOUNT_PATH_PATTERN.matcher(pathInfo).matches();
|
||||
}
|
||||
|
||||
private List<Long> getGlimChildLoanIds(Long loanIdFromRequest) {
|
||||
GroupLoanIndividualMonitoringAccount glimAccount = glimAccountInfoRepository.findOneByIsAcceptingChildAndApplicationId(true,
|
||||
BigDecimal.valueOf(loanIdFromRequest));
|
||||
if (glimAccount != null) {
|
||||
return glimAccount.getChildLoan().stream().map(Loan::getId).toList();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Long> calculateRelevantLoanIds(BodyCachingHttpServletRequestWrapper request) throws IOException {
|
||||
String pathInfo = request.getPathInfo();
|
||||
if (isBatchApi(pathInfo)) {
|
||||
return getLoanIdsFromBatchApi(request);
|
||||
} else {
|
||||
return getLoanIdsFromApi(pathInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> getLoanIdsFromBatchApi(BodyCachingHttpServletRequestWrapper request) throws IOException {
|
||||
List<Long> loanIds = new ArrayList<>();
|
||||
for (BatchRequest batchRequest : getBatchRequests(request)) {
|
||||
// check the URL for Loan related ID
|
||||
String relativeUrl = batchRequest.getRelativeUrl();
|
||||
if (!relativeUrl.contains("$.resourceId")) {
|
||||
// if resourceId reference is used, we simply don't know the resourceId without executing the requests
|
||||
// first, so skipping it
|
||||
loanIds.addAll(getLoanIdsFromApi(relativeUrl));
|
||||
}
|
||||
|
||||
// check the body for Loan ID
|
||||
Long loanId = getTopLevelLoanIdFromBatchRequest(batchRequest);
|
||||
if (loanId != null) {
|
||||
loanIds.add(loanId);
|
||||
}
|
||||
}
|
||||
return loanIds;
|
||||
}
|
||||
|
||||
private Long getTopLevelLoanIdFromBatchRequest(BatchRequest batchRequest) throws JsonProcessingException {
|
||||
String body = batchRequest.getBody();
|
||||
if (StringUtils.isNotBlank(body)) {
|
||||
JsonNode jsonNode = objectMapper.readTree(body);
|
||||
if (jsonNode.has("loanId")) {
|
||||
return jsonNode.get("loanId").asLong();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Long> getLoanIdsFromApi(String pathInfo) {
|
||||
return getLoanIdList(pathInfo);
|
||||
}
|
||||
|
||||
private List<Long> getLoanIdList(String pathInfo) {
|
||||
boolean isGlim = isGlim(pathInfo);
|
||||
Long loanIdFromRequest = getLoanId(isGlim, pathInfo);
|
||||
if (loanIdFromRequest == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (isGlim) {
|
||||
return getGlimChildLoanIds(loanIdFromRequest);
|
||||
} else {
|
||||
return Collections.singletonList(loanIdFromRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.service.MDCWrapper;
|
||||
import org.apache.fineract.infrastructure.instancemode.filter.FineractInstanceModeApiFilter;
|
||||
import org.apache.fineract.infrastructure.jobs.filter.LoanCOBApiFilter;
|
||||
import org.apache.fineract.infrastructure.jobs.filter.LoanCOBFilterHelper;
|
||||
import org.apache.fineract.infrastructure.jobs.filter.ProgressiveLoanModelCheckerFilter;
|
||||
import org.apache.fineract.infrastructure.security.converter.FineractJwtAuthenticationTokenConverter;
|
||||
import org.apache.fineract.infrastructure.security.data.TenantAuthenticationDetails;
|
||||
import org.apache.fineract.infrastructure.security.filter.BusinessDateFilter;
|
||||
@@ -118,6 +119,9 @@ public class AuthorizationServerConfig {
|
||||
@Autowired
|
||||
private BusinessDateReadPlatformService businessDateReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
ProgressiveLoanModelCheckerFilter progressiveLoanModelCheckerFilter;
|
||||
|
||||
@Bean
|
||||
@Order(1)
|
||||
public SecurityFilterChain publicEndpoints(HttpSecurity http) throws Exception {
|
||||
@@ -173,9 +177,12 @@ public class AuthorizationServerConfig {
|
||||
if (!Objects.isNull(loanCOBFilterHelper)) {
|
||||
http.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class) //
|
||||
.addFilterAfter(idempotencyStoreFilter(), LoanCOBApiFilter.class); //
|
||||
http.addFilterBefore(progressiveLoanModelCheckerFilter, LoanCOBApiFilter.class);
|
||||
} else {
|
||||
http.addFilterAfter(idempotencyStoreFilter(), FineractInstanceModeApiFilter.class); //
|
||||
http.addFilterAfter(progressiveLoanModelCheckerFilter, FineractInstanceModeApiFilter.class);
|
||||
}
|
||||
|
||||
if (fineractProperties.getIpTracking().isEnabled()) {
|
||||
http.addFilterAfter(callerIpTrackingFilter(), RequestResponseFilter.class);
|
||||
}
|
||||
|
||||
+3
-1
@@ -33,6 +33,7 @@ import java.util.Collections;
|
||||
import java.util.TreeMap;
|
||||
import org.apache.fineract.cob.COBBusinessStepService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.ProgressiveLoanModelProcessingService;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.StepExecution;
|
||||
import org.springframework.batch.item.ExecutionContext;
|
||||
@@ -41,8 +42,9 @@ import org.springframework.batch.item.ExecutionContext;
|
||||
public class LoanItemProcessorStepDefinitions implements En {
|
||||
|
||||
private COBBusinessStepService cobBusinessStepService = mock(COBBusinessStepService.class);
|
||||
private ProgressiveLoanModelProcessingService progressiveLoanModelProcessingService = mock(ProgressiveLoanModelProcessingService.class);
|
||||
|
||||
private LoanItemProcessor loanItemProcessor = new LoanItemProcessor(cobBusinessStepService);
|
||||
private LoanItemProcessor loanItemProcessor = new LoanItemProcessor(cobBusinessStepService, progressiveLoanModelProcessingService);
|
||||
|
||||
private Loan loan = mock(Loan.class);
|
||||
|
||||
|
||||
+5
-5
@@ -202,15 +202,15 @@ public final class BatchHelper {
|
||||
br.setMethod("POST");
|
||||
|
||||
final String extId;
|
||||
if (externalId.equals("")) {
|
||||
extId = "ext" + String.valueOf((10000 * secureRandom.nextDouble())) + String.valueOf((10000 * secureRandom.nextDouble()));
|
||||
if (externalId.isEmpty()) {
|
||||
extId = UUID.randomUUID().toString();
|
||||
} else {
|
||||
extId = externalId;
|
||||
}
|
||||
|
||||
final String body = "{ \"officeId\": 1, \"legalFormId\":1, \"firstname\": \"Petra\", \"lastname\": \"Yton\"," + "\"externalId\": "
|
||||
+ extId + ", \"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en\","
|
||||
+ "\"active\": false, \"submittedOnDate\": \"04 March 2009\"}";
|
||||
final String body = "{ \"officeId\": 1, \"legalFormId\":1, \"firstname\": \"Petra\", \"lastname\": \"Yton\"," + "\"externalId\": \""
|
||||
+ extId
|
||||
+ "\", \"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en\",\"active\": false, \"submittedOnDate\": \"04 March 2009\"}";
|
||||
|
||||
br.setBody(body);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user