From 071592eceb4a920c0a198d3cd7d4b0dd9c8c5824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Soma=20S=C3=B6r=C3=B6s?= Date: Thu, 24 Apr 2025 15:30:48 +0200 Subject: [PATCH] FINERACT-2220: Use stored ProgressiveLoanInterestScheduleModel for Periodic Accrual Calculation --- .../LoanDownPaymentHandlerServiceImpl.java | 5 +++ ...dableProgressiveLoanScheduleGenerator.java | 39 ++++++++++++++++++- .../ProgressiveLoanScheduleGenerator.java | 16 ++++++-- .../domain/LoanScheduleGeneratorTest.java | 9 ++++- 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java index 8b984321b..9b0a1d830 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java @@ -131,6 +131,11 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler || !DateUtils.isEqualBusinessDate(loanTransaction.getTransactionDate()) || currentInstallment == null || !currentInstallment.getTotalOutstanding(loan.getCurrency()).isEqualTo(loanTransaction.getAmount(loan.getCurrency())); + // TODO FINERACT-2220 fix processLatestTransaction to save model. + if (loan.isProgressiveSchedule() && loan.isInterestBearing()) { + reprocess = true; + } + if (isTransactionChronologicallyLatest && adjustedTransaction == null && (!reprocess || !loan.isInterestBearingAndInterestRecalculationEnabled()) && !loan.isForeclosure()) { loanTransactionProcessingService.processLatestTransaction(loan.getTransactionProcessingStrategyCode(), loanTransaction, diff --git a/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java b/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java index 1b7774976..1c3a19bbd 100644 --- a/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java +++ b/fineract-progressive-loan-embeddable-schedule-generator/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/EmbeddableProgressiveLoanScheduleGenerator.java @@ -19,9 +19,16 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; import java.math.MathContext; +import java.time.LocalDate; +import java.util.Optional; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.ProgressiveLoanModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlan; +import org.apache.fineract.portfolio.loanaccount.service.InterestScheduleModelRepositoryWrapper; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator; +import org.apache.fineract.portfolio.loanproduct.calc.data.ProgressiveLoanInterestScheduleModel; +import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; @SuppressWarnings("unused") public class EmbeddableProgressiveLoanScheduleGenerator { @@ -33,10 +40,40 @@ public class EmbeddableProgressiveLoanScheduleGenerator { public EmbeddableProgressiveLoanScheduleGenerator() { this.emiCalculator = new ProgressiveEMICalculator(); this.scheduledDateGenerator = new DefaultScheduledDateGenerator(); - this.scheduleGenerator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator); + this.scheduleGenerator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator, + new NoopInterestScheduleModelRepositoryWrapper()); } public LoanSchedulePlan generate(final MathContext mc, final LoanRepaymentScheduleModelData modelData) { return scheduleGenerator.generate(mc, modelData); } + + private static final class NoopInterestScheduleModelRepositoryWrapper implements InterestScheduleModelRepositoryWrapper { + + @Override + public Optional findOneByLoanId(Long loanId) { + return Optional.empty(); + } + + @Override + public Optional extractModel(Optional progressiveLoanModel) { + return Optional.empty(); + } + + @Override + public String writeInterestScheduleModel(Loan loan, ProgressiveLoanInterestScheduleModel model) { + return ""; + } + + @Override + public Optional readProgressiveLoanInterestScheduleModel(Long loanId, + LoanProductMinimumRepaymentScheduleRelatedDetail detail, Integer installmentAmountInMultipliesOf) { + return Optional.empty(); + } + + @Override + public Optional getSavedModel(Loan loan, LocalDate businessDate) { + return Optional.empty(); + } + } } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java index caa7ea5ed..5e3f85ffd 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/ProgressiveLoanScheduleGenerator.java @@ -29,6 +29,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.data.CurrencyData; @@ -48,6 +49,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleM import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleParams; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlan; import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.MultiDisbursementOutstandingAmoutException; +import org.apache.fineract.portfolio.loanaccount.service.InterestScheduleModelRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService; import org.apache.fineract.portfolio.loanproduct.calc.EMICalculator; import org.apache.fineract.portfolio.loanproduct.calc.data.OutstandingDetails; @@ -61,12 +63,15 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { private final ScheduledDateGenerator scheduledDateGenerator; private final EMICalculator emiCalculator; + private final InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapper; private LoanTransactionProcessingService loanTransactionProcessingService; - public ProgressiveLoanScheduleGenerator(ScheduledDateGenerator scheduledDateGenerator, EMICalculator emiCalculator) { + public ProgressiveLoanScheduleGenerator(ScheduledDateGenerator scheduledDateGenerator, EMICalculator emiCalculator, + InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapper) { this.scheduledDateGenerator = scheduledDateGenerator; this.emiCalculator = emiCalculator; + this.interestScheduleModelRepositoryWrapper = interestScheduleModelRepositoryWrapper; } @Autowired(required = false) @@ -187,7 +192,10 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { case NONE -> throw new IllegalStateException("Unexpected PreClosureInterestCalculationStrategy: NONE"); }; - ProgressiveLoanInterestScheduleModel model = processor.calculateInterestScheduleModel(loan.getId(), onDate); + Optional savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, + transactionDate); + ProgressiveLoanInterestScheduleModel model = savedModel + .orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), onDate)); OutstandingDetails outstandingAmounts = emiCalculator.getOutstandingAmountsTillDate(model, transactionDate); // TODO: We should add all the past due outstanding amounts as well OutstandingAmountsDTO result = new OutstandingAmountsDTO(currency) // @@ -228,7 +236,9 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator { if (installment.isAdditional() || installment.isDownPayment() || installment.isReAged()) { return Money.zero(loan.getCurrency()); } - ProgressiveLoanInterestScheduleModel model = processor.calculateInterestScheduleModel(loan.getId(), targetDate); + Optional savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate); + ProgressiveLoanInterestScheduleModel model = savedModel + .orElseGet(() -> processor.calculateInterestScheduleModel(loan.getId(), targetDate)); return emiCalculator.getPeriodInterestTillDate(model, installment.getDueDate(), targetDate, false); } diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java index e56927d6b..552c52c80 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGeneratorTest.java @@ -33,6 +33,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleP import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDisbursementPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanDownPaymentPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePlanRepaymentPeriod; +import org.apache.fineract.portfolio.loanaccount.service.InterestScheduleModelRepositoryWrapper; import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService; import org.apache.fineract.portfolio.loanproduct.calc.ProgressiveEMICalculator; import org.junit.jupiter.api.Test; @@ -55,6 +56,8 @@ class LoanScheduleGeneratorTest { private static final MathContext mc = new MathContext(12, RoundingMode.HALF_EVEN); private static final BigDecimal DOWN_PAYMENT_PORTION = BigDecimal.valueOf(25); private static final LoanTransactionProcessingService loanTransactionProcessingService = mock(LoanTransactionProcessingService.class); + private static final InterestScheduleModelRepositoryWrapper interestScheduleModelRepositoryWrapperMock = mock( + InterestScheduleModelRepositoryWrapper.class); @Test void testGenerateLoanSchedule() { @@ -63,7 +66,8 @@ class LoanScheduleGeneratorTest { NOMINAL_INTEREST_RATE, false, DaysInMonthType.DAYS_30, DaysInYearType.DAYS_360, null, null, null, false, null); ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); - ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator); + ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator, + interestScheduleModelRepositoryWrapperMock); generator.setLoanTransactionProcessingService(loanTransactionProcessingService); LoanSchedulePlan loanSchedule = generator.generate(mc, modelData); @@ -100,7 +104,8 @@ class LoanScheduleGeneratorTest { null); ScheduledDateGenerator scheduledDateGenerator = new DefaultScheduledDateGenerator(); - ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator); + ProgressiveLoanScheduleGenerator generator = new ProgressiveLoanScheduleGenerator(scheduledDateGenerator, emiCalculator, + interestScheduleModelRepositoryWrapperMock); generator.setLoanTransactionProcessingService(loanTransactionProcessingService); LoanSchedulePlan loanSchedule = generator.generate(mc, modelData);