FINERACT-2421: Allow ProgressiveLoanInterestScheduleModel recalculation
Co-authored-by: Soma Sörös <soma.soros@dpc.hu>
This commit is contained in:
+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>
|
||||
Reference in New Issue
Block a user